Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 356
Daniel Jennings Avatar asked Jun 04 '21 19:06

Daniel Jennings


People also ask

Can ternary operator have 3 conditions?

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.

Can ternary operator have multiple conditions?

We can nest ternary operators to test multiple conditions.

What is the difference between conditional operator and ternary operator?

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.

Is ternary faster than if-else?

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.


1 Answers

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 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.

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.

like image 128
T.C. Avatar answered Oct 12 '22 07:10

T.C.