В чем преимущество std::enable_if перед static_assert для шаблонов классов?

Меня интересовало преимущество std::enable_if над static_asserts, чтобы предотвратить создание экземпляра шаблона. Этот ответ предполагает, что std::enable_if позволяет SFINAE, что является убедительным аргументом в случае шаблоны функций.

Однако допустим ли этот аргумент для шаблонов классовшаблоны переменных)? Насколько я знаю, там не задействовано разрешение перегрузки, что делает SFINAE - опять же, насколько я знаю - неактуальным, но я могу ошибаться. Если да, то можете ли вы назвать пример?

Если нет, я предполагаю, что static_assert будет лучшим решением для данной проблемы (предотвращение создания экземпляра шаблона) в случае шаблонов классов, так как это, возможно, более явное, краткое и читаемое и позволяет создавать собственное сообщение об ошибке. Это правильно или я упускаю что-то кроме SFINAE?


person Reizo    schedule 31.08.2020    source источник
comment
static_assert не предотвращает создание экземпляра, если условие не выполняется. Вместо этого это делает программу плохо сформированной.   -  person n. 1.8e9-where's-my-share m.    schedule 31.08.2020
comment
@Elliott хммм...   -  person Fureeish    schedule 31.08.2020
comment
@n.'местоимения'м., разве сбой static_assert не должен вызывать сбой компиляции? Как это плохо сформировано?   -  person Elliott    schedule 31.08.2020
comment
@Elliott Так сказано в стандарте. Если значение выражения при таком преобразовании истинно, объявление не действует. В противном случае программа некорректна   -  person n. 1.8e9-where's-my-share m.    schedule 31.08.2020
comment
@n.'местоимения'm., я не понимал, что это означает неправильное оформление. Спасибо. (Я всегда думал, что это означает, что эффект программы не определен)   -  person Elliott    schedule 31.08.2020


Ответы (3)


Пример того, как перегружать классы с помощью SFINAE:

#include <type_traits>
#include <iostream>

template <typename, typename = void>
struct Foo;

template <typename Bar>
struct Foo <Bar, typename std::enable_if<std::is_same<Bar,int>::value>::type>
{
    Foo ()
    {
        std::cout << "Hello ";
    }
};

template <typename Bar>
struct Foo <Bar, typename std::enable_if<std::is_same<Bar,bool>::value>::type>
{
    Foo ()
    {
        std::cout << "world!\n";
    }
};

int main()
{
    Foo<int>();
    Foo<bool>();
}

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

template <typename Bar>
class Foo
{
    static_assert(
        std::is_same<Bar,int>::value || 
        std::is_same<Bar,bool>::value, 
        "Bar must be bool or int");
    
    // class stuff
};

... понятнее и проще, чем:

template <typename Bar,
    typename std::enable_if<
    std::is_same<Bar,int>::value ||
    std::is_same<Bar,bool>::value,
    bool>::type = true>
class Foo
{
    // class stuff
};

...особенно потому, что со вторым вариантом компилятор обращается к Foo объектам с их первым аргументом и их вторым аргументом (true в нашем случае), что может быть загадочным для пользователя. Кроме того, это более запутанно, если вы хотите, чтобы заголовок обрабатывал класс только с его упрощенным объявлением (см., как это сделать здесь).

person Elliott    schedule 31.08.2020
comment
яснее и проще (в случае отсутствия перегрузки) и не создает неудобств, подобных предотвращению SFINAE при разрешении перегрузки функции, верно? - person Reizo; 31.08.2020
comment
Nvm, я понял, что измеряю двойными стандартами с моим предыдущим комментарием. Нет никаких недостатков в использовании static_assert в шаблоне функции, если я не стремлюсь к перегрузке. Так что аналогия класса и шаблона функции вполне полная. - person Reizo; 31.08.2020
comment
@ Reizo, да, меня немного смутил твой вопрос. Если вы используете static_assert вместо enable_if, это предотвратит перегрузку (только на основе этого шаблона [вы все равно можете перегружаться на основе других аргументов/шаблонов]) - person Elliott; 31.08.2020

Однако законен ли этот аргумент для шаблонов классов (и шаблонов переменных)? Насколько я знаю, там не задействовано разрешение перегрузки, что делает SFINAE - опять же, насколько я знаю - неактуальным, но я могу ошибаться. Если да, то можете ли вы назвать пример?

Вы можете специализировать шаблоны классов, а SFINAE можно использовать для выбора между специализациями. Это также предотвратит создание экземпляра такого (к тому времени, возможно, неправильно сформированного) класса / его специализации, вместо того, чтобы не скомпилировать из-за static_assert.

person Fureeish    schedule 31.08.2020
comment
Я не знаю, как спецификация типа шаблона может быть двусмысленной, требуя «выбора между [возможными] специализациями». Не могли бы вы добавить пример к своему ответу? - person Reizo; 31.08.2020

std::enabled_if используется в SFIANE для специализации классов шаблонов, методов и .... static_assert используется для проверки контракта во время компиляции и предоставления полезного сообщения об ошибке.

person apramc    schedule 31.08.2020