Question

I have been using (and seen used) static_assert to flag undesired values of template parameter values. However, for all cases I came across it seems better and more elegant to disable those undesired values via SFINAE.

For example:

template<typename T,
         class = std::enable_if<std::is_floating_point<T>::value>::type>
struct Foo { ... };

instead of:

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

So my question: when should I use static_assert instead of SFINAE and why?

EDIT:

I think what I've learned so far is the following:

1. SFINAE is a versatile and powerful but potentially very complicated tool that can be used for many tasks, including function overload resolution (which some seem to regard as its only purpose).

2. SFINAE can be used in a relatively simple way wherever static_assert can, except that it appears in the declaration (of a class or function) rather than its definition (or is is possible to insert a static_assert into, say, a class forward declaration?). That makes more verbatim and hence clearer code. However, because SFINAE is complicated, it tends to be harder to get right than a simple static_assert.

3. On the other hand static_assert has the benefit of a clearer compiler error message, which some seem to regard as the main purpose of both.

Was it helpful?

Solution

I think static_assert is the right choice if you want to enforce that T is a floating point type. This method states your intent more clearly than the SFINAE solution.

OTHER TIPS

You use SFINAE, if you want another overload to be used, and static_assert if none of them would fit such parameter.

static_assert makes the compilation fail. SFINAE allows you to remove one possible overload.

For one, using SFINAE may lead to another overload being picked that was originally a worse match and wouldn't be considered.

And in the situation that there are other overloads, but non of them is viable, you get some nice things like this:

#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()); }

Output:

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:
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top