Тернарный оператор + конструктор C ++ 11 из initializer_list - PullRequest
24 голосов
/ 02 апреля 2012

При разработке приложения у меня возникла следующая проблема. Я хотел вернуть пустой std::list<string>, когда указатель на указанную функцию был нулевым, или результат этой функции в противном случае. Это упрощенная версия моего кода:

typedef std::list<std::string> (*ParamGenerator)();

std::list<std::string> foo() {
    /* ... */
    ParamGenerator generator = ...;
    if(generator)
        return generator();
    else
        return {};
}

Однако в этих случаях мне обычно нравится использовать троичный оператор (?:), поэтому я попытался использовать его следующим образом (как обычно):

return generator ? generator() : {};

Но получил эту ошибку:

somefile.cpp:143:46: error: expected primary-expression before ‘{’ token
somefile.cpp:143:46: error: expected ‘;’ before ‘{’ token

Значит ли это, что я не могу использовать троичный оператор для возврата объектов, созданных с использованием их конструктора из initializer_list? Есть ли какая-то конкретная причина для этого?

Ответы [ 4 ]

18 голосов
/ 02 апреля 2012

Стандартные записи в 8.5.4.1: инициализация списка

Примечание: можно использовать инициализацию списка

  • как инициализатор в определении переменной (8.5)
  • как инициализатор в новом выражении (5.3.4)
  • в операторе возврата (6.6.3)
  • в качестве аргумента функции (5.2.2)
  • в качестве индекса (5.2.1)
  • в качестве аргумента для вызова конструктора (8.5, 5.2.3)
  • как инициализатор для нестатического элемента данных (9.2)
  • в mem-инициализаторе (12.6.2)
  • в правой части задания (5.17)

Ничто из них не является троичным оператором. Более минималистичный return 1?{}:{}; тоже недопустим, то, что вы хотите, невозможно.

Конечно, вы можете явно вызвать конструктор std::list<std::string>{}, но я бы порекомендовал записать if - else -блок, как вы уже сделали.

7 голосов
/ 02 апреля 2012

Когда вы делаете {}, компилятор не знает типа, который вы ожидаете, так что это просто бессмысленное выражение, с которым компилятор не знает, что делать. Обе стороны : оцениваются отдельно, и только тогда компилятор будет жаловаться, если типы не совпадают. Я бы просто сделал это:

return generator ? generator() : std::list<std::string>();
2 голосов
/ 03 апреля 2012

Если вам действительно нравится троичный оператор, вы можете попробовать что-то вроде этого:

return generator ? generator() : decltype(generator()) { "default value", "generator was empry" };

это будет работать, даже если вы позже измените типы возврата.

0 голосов
/ 09 февраля 2019

Другая возможность - определить функцию-обертку для условного оператора:

template<class T> T& conditional(bool b, T&x, T&y) { return b ? x : y; }
template<class T> const T& conditional(bool b, const T&x, const T&y) { return b ? x : y; }

Это позволяет вам звонить:

return conditional(generator, generator(), {});
...