Как условный оператор (?:) работает в С++?

Я написал фрагмент кода ниже:

#include <string>

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

(см. здесь)

К сожалению, это вызывает logic_error:

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

Я хочу знать причину, по которой компилятор создает строковый объект?

Ответ 1

Тернарный оператор эффективно работает следующим образом:

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);
}

В приведенном выше случае вы не храните значение, но необходимо отметить несколько условий:

  • И истинный случай, и ложный случай должны быть одного типа (или быть конвертируемыми в один и тот же тип).
  • Он по-прежнему работает, даже если тип не имеет конструктора по умолчанию (поэтому он более сложный, чем выше).

Проблема в том, что ваш код ожидает, что тип будет std::string, в зависимости от типа в истинном случае. Тип в ложном случае является литералом, литералом, который можно считать эквивалентным NULL, и поэтому его можно считать a const char*, который можно конвертировать в std::string. Если вы попытаетесь построить std::string из nullptr, он выдает исключение выше.

Это на самом деле довольно тонко, поскольку, если вы используете любой целочисленный литерал, отличный от 0, компилятор выдает ошибку:

#include <string>


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

Будьте осторожны с неявными преобразованиями. Ошибка нулевой проверки и времени выполнения довольно изящна и впоследствии избавила вас от тонких ошибок или сбоев (почти наверняка segfault).

Ответ 2

Итак, я хочу знать причину, почему компилятор строит строковый объект?

Потому что std::string предоставляет неявный конструктор, который принимает простой const char*, ожидающий NUL завершенную строку c-style.

В этом случае утверждение runtime о том, что с этим конструктором передано nullptr (== 0), поэтому вы видите исключение.


В качестве примечания:

Ваше выражение

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

может быть оптимизирован достойным компилятором, поскольку он не имеет значительных побочных эффектов.
Таким образом, это исключение, которое вы видите, может уйти со строками с использованием другого уровня оптимизации.