Могу ли я использовать declval для создания неиспользованного возврата? - PullRequest
1 голос
/ 08 апреля 2019

Допустим, у меня есть шаблонизированная функция, которая будет специализированной, поэтому меня не волнует базовая реализация. Могу ли я сделать что-то вроде этого:

template <typename T>
T dummy() {
    assert(false);
    return declval<T>();
}

Когда я пытаюсь сделать это в Я получаю сообщение об ошибке:

неразрешенный внешний символ char const && __cdecl std::declval<char const >(void) (?? $ declval @ $$ CBD @ std @@ YA $$ QEBDXZ), на который есть ссылка в функции char const __cdecl dummy<char const>()

Опять же, эта функция не вызывается, но я сохраняю указатель на нее. Вместо этого я могу использовать return T{}, и он компилируется, но мне нужно, чтобы это работало, даже если для T нет конструктора по умолчанию. Есть ли способ, которым я могу обойти это?

Ответы [ 2 ]

3 голосов
/ 08 апреля 2019

Вы можете обойти проблему, не предоставив определения для шаблона функции. Использование

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. Это означает, что вам не нужно беспокоиться о возврате.


Вы можете использовать Eve

template <typename T>
T dummy() = delete;

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

1 голос
/ 08 апреля 2019

Insttiating dummy будет использовать std::declval<T>, что не разрешено стандартом.

Обратите внимание, что это не ошибка компиляции - просто опустить оператор return. Это просто приводит к UB, если вызывается dummy. Поскольку вы уверены, что dummy никогда не будет вызван, это не должно создавать для вас никаких проблем.

Однако, возможно, вам следует избегать, чтобы компилятор выдавал предупреждение о том, что элемент управления достигает конца функции, не являющейся void. В конце концов, в не отладочной сборке конец функции будет достигнут , если вам случится вызвать dummy, поскольку assert исчезнет. В этом случае я бы предложил поставить после assert:

throw std::logic_error("dummy should not be called");

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

Это также повышает вероятность того, что программа действительно будет аварийно завершена, если dummy действительно будет вызван каким-либо образом, а не вызывать UB.

...