The following code compiles under Clang/GCC under the C++17 standard, but does not compile under MSVC with -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;
}
Link to godbolt's compiler explorer with all three: https://godbolt.org/z/6d5Mrjnd7
MSVC emits an error for each of those four lines:
<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'
Whereas Clang/GCC each call the CStringPtr constructor for the naked string for all four of those cases.
In the MSVC /Zc:ternary documentation they claim that the flag enables standards-compliant resolution of the ternary operator, which implies that either there's a bug in MSVC's implementation or Clang/GCC are not standards-compliant here.
The one other note here is that the MSVC docs mention an exception in this exact case with respect to the const char *
type being used:
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.
So, is MSVC non-compliant under C++17 rules? Or are Clang/GCC?
The conditional (ternary) operator is the only JavaScript operator that takes three operands: a condition followed by a question mark ( ? ), then an expression to execute if the condition is truthy followed by a colon ( : ), and finally the expression to execute if the condition is falsy.
We can nest ternary operators to test multiple conditions.
The conditional operator takes three operands, so it is a ternary operator. The conditional operator is the only ternary operator available in the C programming language, so the names ternary operator and conditional operator are used alternatively to mean the conditional operator.
A ternary operator is a single statement, while an if-else is a block of code. A ternary operator is faster than an if-else block.
This is Core issue 1805, which changed ?:
to decay arrays and functions to pointers before testing whether the other operand can be converted to it. The example in that issue is basically the one in your question:
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 fromconst char*
. However, the target type for the conversion ofs
isconst char[1]
, notconst char*
, so that conversion fails and the result of the conditional-expression has typeS
.
It appears that neither GCC nor Clang implements that issue resolution, so they are still testing whether CStringPtr
can be converted to an array.
If you manually decay the string literals with a unary +
, everyone rejects the example as ambiguous.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With