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

C++17 conditional (ternary) operator inconsistency between MSVC and Clang/GCC

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

下无法在 MSVC 下编译
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;
}

Link 到 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'

而 Clang/GCC 对于所有这四种情况,每个都为裸字符串调用 CStringPtr 构造函数。

MSVC /Zc:ternary documentation 中,他们声称该标志启用了三元运算符的符合标准的解析,这意味着要么 MSVC 的实现中存在错误,要么 Clang/GCC 这里不符合标准。

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

An important exception to this common pattern is when the type of the operands is one of the null-terminated string types, such as const char*, const char16_t*, and so on. You can also reproduce the effect with array types and the pointer types they decay to. The behavior when the actual second or third operand to ?: is a string literal of corresponding type depends on the language standard used. C++17 has changed semantics for this case from C++14.

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

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

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

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

One might expect that the expression in #1 would be ambiguous, since S can be converted both to and from const char*. However, the target type for the conversion of s is const char[1], not const char*, so that conversion fails and the result of the conditional-expression has type S.

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

如果您使用一元 + 手动衰减字符串文字,每个人都会拒绝该示例。