何时使用`static_assert`而不是SFINAE?

Wal*_*ter 24 c++ templates static-assert sfinae c++11

我一直在使用(并且看到用过)static_assert来标记模板参数值的不需要的值.然而,对于我遇到的所有情况,通过SFINAE禁用那些不需要的值似乎更好,更优雅.例如

template<typename T,
         class = std::enable_if<std::is_floating_point<T>::value>::type>
struct Foo { ... };
Run Code Online (Sandbox Code Playgroud)

代替

template<typename T>
struct Foo {
  static_assert(std::is_floating_point<T>::value,
                "Foo<T>: T must be floating point :-(");
  ...
};
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:何时使用static_assert而不是SFINAE?为什么?

编辑

我认为到目前为止我所学到的是以下内容

1 SFINAE是一个多功能且功能强大但可能非常复杂的工具,可用于许多任务,包括功能过载解决(有些人认为这是其唯一目的).

2 SFINAE可以以一种相对简单的方式使用static_assert,除非它出现在(类或函数的)声明中而不是它的定义(或者是否可以插入static_assert一个类,例如,类前向声明​​?) .这使得更加逐字,更清晰的代码.然而,因为SFINAE很复杂,所以它往往比简单的更难static_assert.

3另一方面,static_assert有一个更清晰的编译器错误消息的好处,有些人似乎认为这两者的主要目的.

Tad*_*pec 14

如果您想要使用另一个重载,并且static_assert如果它们都不适合这样的参数,则使用SFINAE .


log*_*og0 11

static_assert使编译失败.SFINAE允许您删除一个可能的过载.


Sta*_*ked 8

static_assert如果你想强制执行T浮点类型,我认为是正确的选择.此方法比SFINAE解决方案更清楚地表明您的意图.


Xeo*_*Xeo 6

例如,使用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()); }
Run Code Online (Sandbox Code Playgroud)

输出:

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:
Run Code Online (Sandbox Code Playgroud)

  • SFINAE的一个令人讨厌的属性是你需要确保多个重载具有非重叠条件,否则你仍然会收到错误.我非常喜欢标签调度以进行选择性重载. (3认同)
  • @Luc"仅适用于功能模板".事实并非如此.SFINAE在类模板部分特化的参数推断期间也处于活动状态. (3认同)