27

我一直在使用(并且看到使用)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,编译器错误消息更清晰,有些人似乎认为这是两者的主要目的。

4

4 回答 4

19

如果你想使用另一个重载,并且static_assert它们都不适合这样的参数,你可以使用 SFINAE。

于 2012-08-16T10:07:38.817 回答
14

static_assert使编译失败。SFINAE 允许您消除一种可能的过载。

于 2012-08-16T10:02:33.393 回答
9

static_assert如果您想强制T使用浮点类型,我认为这是正确的选择。这种方法比 SFINAE 解决方案更清楚地说明了您的意图。

于 2012-08-16T10:43:16.093 回答
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()); }

输出:

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:
于 2012-08-16T10:09:37.990 回答