Могу ли я использовать declval для создания неиспользованного возврата?
Допустим, у меня есть шаблонизированная функция, которая будет специализированной, поэтому меня не волнует базовая реализация. Могу ли я сделать что-то вроде этого:
template <typename T>
T dummy() {
assert(false);
return declval<T>();
}
Когда я пытаюсь сделать это в visual-studio-2017, я получаю ошибку компоновки:
неразрешенный внешний символ
char const && __cdecl std::declval<char const >(void)
(??$declval@$$CBD@std@@YA$$QEBDXZ) упоминается в функцииchar const __cdecl dummy<char const>()
Опять же, эта функция не вызывается, но я сохраняю указатель на нее. я могу использовать return T{}
вместо этого, и это компилируется, но мне нужно, чтобы это работало, даже если нет конструктора по умолчанию для T
, Есть ли способ, которым я могу обойти это?
2 ответа
Вы можете обойти проблему, не предоставив определения для шаблона функции. С помощью
template <typename T>
T dummy();
template <>
int dummy() { std::cout << "template <> int dummy()"; return 42;}
int main()
{
dummy<int>();
dummy<double>();
return 0;
}
Вы получите ошибку компоновщика, потому что dummy<double>();
не существует, но если вы закомментируете его, код скомпилируется, потому что существует специализация для int
, Это означает, что вам не нужно беспокоиться о возврате.
Вы можете использовать накануне
template <typename T>
T dummy() = delete;
вместо того, чтобы не давать определение, вместо ошибки компоновщика вы получите "красивую" ошибку компилятора, говорящую о том, что вы пытаетесь использовать удаленную функцию. Это также позволяет записывать перегрузки вместо специализаций, что является предпочтительным, поскольку специализации не учитываются при разрешении перегрузки. Это не совсем возможно в вашем случае, так как вы не берете никаких параметров, но если вы это сделаете, вы должны рассмотреть это.
Инстанцирование dummy
будет одр-использовать std::declval<T>
, что не разрешено стандартом.
Обратите внимание, что это не ошибка компиляции - просто опустить оператор return. Это просто приводит к UB, если dummy
называется. Поскольку вы уверены, что dummy
никогда не будет звонить, это не должно создавать никаких проблем для вас.
Однако, возможно, вам следует избегать, чтобы компилятор выдавал предупреждение о том, что элемент управления достигает конца не пустой функции. В конце концов, в не отладочной сборке конец функции будет достигнут, если вам случится вызвать dummy
, так как assert
исчезнет. В этом случае я бы предложил поставить после assert
:
throw std::logic_error("dummy should not be called");
Теперь компилятор должен увидеть, что функция не может достичь конца своего тела, не возвращая значение, поскольку она вообще не может достичь конца.
Это также повышает вероятность сбоя программы, если dummy
на самом деле вызывается как-то, а не вызывая UB.