G.yang
G.yang

Reputation: 357

How does the conditional operator (?:) work in C++?

I have written the code snippet below:

#include <string>

int main() {
    std::string str = "test";
    (str == "tes") ? str.replace(0, 1, "T") : 0;
}

(See here)

Unfortunately, it causes a logic_error:

terminate called after throwing an instance of 'std::logic_error'
what():  basic_string::_S_construct NULL not valid

I want to know the reason why the compiler constructs a string object?

Upvotes: 12

Views: 1001

Answers (2)

user0042
user0042

Reputation: 8018

so,i want to know the reason why the compiler construct a string object?

Because std::string provides an implicit constructor that takes a plain const char* expecting a NUL terminated c-style string.

There's a runtime assertion that there's no nullptr (== 0) passed with that constructor, hence the exception you see.


As a side note:

Your statement

(str == "tes") ? str.replace(0, 1, "T") : 0;

might be optimized out by a decent compiler, because it doesn't have any significant side effects.
So that exception you see might go away with builds using a different optimization level.

Upvotes: 0

Alex Huszagh
Alex Huszagh

Reputation: 14614

The ternary operator effectively works like follows:

std::string str = "test";
std::string _;    // using _ since you don't store the variable
if (str == "tes") {
    _ = str.replace(0, 1, "T");
} else {
    _ = 0;  // this calls std::string(nullptr);
}

In the case above, you don't store the value, but a few conditions must be noted:

  1. Both the true case and the false case must be the same type (or be convertible to the same type).
  2. It still works even if the type does not have a default constructor (so it's more elaborate than the above).

The issue here is that your code expects the type to be std::string, based on the type in the true-case. The type in the false-case is a literal, a literal that can be considered equivalent to NULL, and therefore can be considered a const char* which is convertible to std::string. If you try to construct a std::string from a nullptr, it throws the above exception.

This is actually quite subtle, since if you use any integer literal other than 0, the compiler will throw an error:

#include <string>


int main()
{
    std::string s(0);           // 0 is comparable to NULL
//    std::string s1(1);        // compiler error: no constructor found
    return 0;
}

Be careful with implicit conversions. The null check and runtime error is quite graceful, and saved you from subtle errors or crashes (almost certainly a segfault) later on.

Upvotes: 17

Related Questions