Ответ 1
Я думаю, что static_assert
является правильным выбором, если вы хотите обеспечить, чтобы T
был типом с плавающей точкой. Этот метод более четко определяет ваше намерение, чем решение SFINAE.
Я использовал (и видел) static_assert
для обозначения нежелательных значений значений параметров шаблона. Однако для всех случаев, с которыми я столкнулся, кажется лучше и элегантнее отключать эти нежелательные значения через SFINAE. Например
template<typename T,
class = std::enable_if<std::is_floating_point<T>::value>::type>
struct Foo { ... };
вместо
template<typename T>
struct Foo {
static_assert(std::is_floating_point<T>::value,
"Foo<T>: T must be floating point :-(");
...
};
Итак, мой вопрос: когда использовать static_assert
вместо SFINAE и почему?
ИЗМЕНИТЬ
Я думаю, что я узнал до сих пор следующее
1 SFINAE - это универсальный и мощный, но потенциально очень сложный инструмент, который может использоваться для многих задач, включая разрешение функции перегрузки (которое, по-видимому, рассматривается как единственная цель).
2. SFINAE может использоваться относительно простым способом, когда когда-либо static_assert
может, за исключением того, что он появляется в объявлении (класса или функции), а не в его определении (или возможно вставить static_assert
в, скажем, декларацию класса вперед?). Это делает более дословным и, следовательно, более четким кодом. Однако, поскольку SFINAE сложна, имеет тенденцию быть более сложным, чем простой static_assert
.
3 С другой стороны, static_assert
имеет преимущество более четкого сообщения об ошибке компилятора, которое, по-видимому, рассматривается как основная цель.
Я думаю, что static_assert
является правильным выбором, если вы хотите обеспечить, чтобы T
был типом с плавающей точкой. Этот метод более четко определяет ваше намерение, чем решение SFINAE.
Вы используете SFINAE, если хотите использовать другую перегрузку, и static_assert
, если ни один из них не поместит такой параметр.
static_assert
приводит к сбою компиляции. SFINAE позволяет удалить одну возможную перегрузку.
Во-первых, использование SFINAE может привести к тому, что будет выбрана другая перегрузка, которая была изначально хуже и не учитывалась.
И в ситуации, когда есть другие перегрузки, но не из них жизнеспособны, вы получаете такие приятные вещи, как это:
#include <type_traits>
void f(int){}
void f(bool){}
void f(char){}
void f(float){}
void f(long){}
void f(double){}
void f(short){}
void f(unsigned){}
void f(void*){}
void f(void (*)()){}
template<class C, class T = int>
using EnableIf = typename std::enable_if<C::value, T>::type;
template<class T>
struct sfinae_false : std::false_type{};
template<class T>
void f(T&&, EnableIf<sfinae_false<T>> = 0){}
int main(){ struct X{}; f(X()); }
Вывод:
source.cpp: In function 'int main()':
source.cpp:23:30: error: no matching function for call to 'f(main()::X)'
source.cpp:23:30: note: candidates are:
source.cpp:3:6: note: void f(int)
source.cpp:3:6: note: no known conversion for argument 1 from 'main()::X' to 'int'
source.cpp:4:6: note: void f(bool)
source.cpp:4:6: note: no known conversion for argument 1 from 'main()::X' to 'bool'
source.cpp:5:6: note: void f(char)
source.cpp:5:6: note: no known conversion for argument 1 from 'main()::X' to 'char'
source.cpp:6:6: note: void f(float)
source.cpp:6:6: note: no known conversion for argument 1 from 'main()::X' to 'float'
source.cpp:7:6: note: void f(long int)
source.cpp:7:6: note: no known conversion for argument 1 from 'main()::X' to 'long int'
source.cpp:8:6: note: void f(double)
source.cpp:8:6: note: no known conversion for argument 1 from 'main()::X' to 'double'
source.cpp:9:6: note: void f(short int)
source.cpp:9:6: note: no known conversion for argument 1 from 'main()::X' to 'short int'
source.cpp:10:6: note: void f(unsigned int)
source.cpp:10:6: note: no known conversion for argument 1 from 'main()::X' to 'unsigned int'
source.cpp:11:6: note: void f(void*)
source.cpp:11:6: note: no known conversion for argument 1 from 'main()::X' to 'void*'
source.cpp:12:6: note: void f(void (*)())
source.cpp:12:6: note: no known conversion for argument 1 from 'main()::X' to 'void (*)()'
source.cpp:21:6: note: template<class T> void f(T&&, EnableIf<sfinae_false<T> >)
source.cpp:21:6: note: template argument deduction/substitution failed: