MSVC 和 Clang/GCC 之间的 C++17 条件(三元)运算符不一致

Dan*_*ngs 7 c++ visual-c++ language-lawyer c++17

以下代码在 C++17 标准下的 Clang/GCC 下编译,但在带有-std:C++17 /Zc:ternary.

struct CStringPtr
{
    const char *m_pString = nullptr;

    CStringPtr() = default;
    CStringPtr( const char *pString ) : m_pString( pString ) { }

    operator const char *() const { return m_pString; }
};

int main( int argc, char ** )
{
    bool b = !!argc;

    const char *X = b ? CStringPtr( "inside" ) : "naked";
    const char *Y = b ? "naked" : CStringPtr( "inside" );
    CStringPtr Z = b ? CStringPtr( "inside" ) : "naked";
    CStringPtr W = b ? "naked" : CStringPtr( "inside" );

    // Silence unused-variable warnings.
    return X && Y && Z && W;
}
Run Code Online (Sandbox Code Playgroud)

链接到 godbolt 的编译器资源管理器与所有三个:https ://godbolt.org/z/6d5Mrjnd7

MSVC 为这四行中的每一行发出错误:

<source>(19): error C2445: result type of conditional expression is ambiguous: types 'const char [6]' and 'CStringPtr' can be converted to multiple common types
<source>(19): note: could be 'const char *'
<source>(19): note: or       'CStringPtr'
Run Code Online (Sandbox Code Playgroud)

而 Clang/GCC 在所有四种情况下都为裸字符串调用 CStringPtr 构造函数。

MSVC /Zc:ternary 文档中,他们声称该标志支持符合标准的三元运算符解析,这意味着 MSVC 的实现中存在错误或 Clang/GCC 在这里不符合标准。

这里的另一个注意事项是 MSVC 文档在这种确切情况下提到了与const char *所使用类型有关的例外:

这种常见模式的一个重要例外是当操作数的类型是空终止字符串类型之一时,例如 const char*、const char16_t* 等。您还可以使用数组类型和它们衰减到的指针类型重现效果。当实际的第二个或第三个操作数 ?: 是相应类型的字符串文字时的行为取决于所使用的语言标准。C++17 已经从 C++14 更改了这种情况下的语义。

那么,MSVC 是否不符合 C++17 规则?还是 Clang/GCC?

T.C*_*.C. 7

这是核心问题 1805,它更改?:为在测试其他操作数是否可以转换为指针之前衰减数组和函数。该问题中的示例基本上是您问题中的示例:

  struct S {
    S(const char *s);
    operator const char *();
  };

  S s;
  const char *f(bool b) {
    return b ? s : "";   // #1
  }
Run Code Online (Sandbox Code Playgroud)

人们可能会认为 #1 中的表达式是不明确的,因为 S可以转换为 和 from const char*。但是,转换的目标类型sconst char[1],不是const char*,因此转换失败并且条件表达式的结果具有类型S

似乎 GCC 和 Clang 都没有实现该问题的解决方案,因此他们仍在测试是否CStringPtr可以转换为数组。

如果您手动使用一元对字符串文字进行衰减+,则每个人都会认为该示例不明确。