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

7
以下代码在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;
}

带有三种编译器的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文档中,他们声称该标志可以启用符合标准的三元运算符解析,这意味着MSVC的实现存在错误或者Clang/GCC在此处不符合标准要求。

还有一个需要注意的地方是,在这种具体情况下,MSVC文档提到了与使用const char *类型相关的异常:

重要的例外是当操作数的类型为其中之一的以空字符结尾的字符串类型(例如const char*、const char16_t*等)时。您也可以使用数组类型及其衰减的指针类型来复制效果。当实际的第二个或第三个操作数为相应类型的字符串字面值时,其行为取决于所使用的语言标准。C++17从C++14中更改了此案例的语义。

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

1个回答

7
这是 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都没有实现该问题的解决,因此他们正在测试是否可以将转换为数组。

如果您使用一元符号+手动衰减字符串字面量,则所有人都会拒绝示例,因为它不明确。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接