Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static assertions and SFINAE

Consider this:

template <typename T>
struct hash
{
     static_assert(false,"Not implemented.");
};

struct unhashable {};

template <typename T>
auto test(const T &t) -> decltype((*(hash<T> const *)nullptr)(t),int);

void test(...);

int main()
{
    std::cout << std::is_same<decltype(test(std::declval<unhashable>())),void>::value;
}

Apart from obviously missing headers, should this compile?

In other words, I am asking if the static assertion failure triggering inside a trailing decltype while deducing the return value of an overloaded function template is requested to halt the compilation, or if the overload has simply to be discarded.

On gcc 4.7, compilation fails. I am pretty positive though that this will compile ok in gcc 4.8 (but cannot check at this very moment). Who is right?

like image 992
bluescarni Avatar asked Apr 30 '13 14:04

bluescarni


People also ask

What is a static assertion?

What is static assertion? Static assertions are a way to check if a condition is true when the code is compiled. If it isn't, the compiler is required to issue an error message and stop the compiling process. The condition that needs to be checked is a constant expression. Performs compile-time assertion checking.

What is static assert in C?

static_assert is used to ensure that a condition is true when the code is compiled. The condition must be a constant expression. In case of failure, an error message is displayed to alert the user. Here's the syntax: static_assert(expression, message); Flow of Events with a static assertion.


2 Answers

The compilation has to fail in any compliant compiler.

SFINAE rules are based on declarations and not definitions. (Sorry if I'm using the wrong terminology here.) What I mean is the following:

For a class/struct:

template < /* substitution failures here are not errors */ >
struct my_struct {
    // Substitution failures here are errors.
};

For a function:

template </* substitution failures here are not errors */>
/* substitution failures here are not errors */
my_function( /* substitution failures here are not errors */) {
    /* substitution failures here are errors */
}

In addition, the non existence of the struct/function for the given set of template arguments is also subject to SFINAE rules.

Now a static_assert can only appear in the regions where substitution failures are errors and, therefore, if it fires, you'll get a compiler error.

For instance the following would be a wrong implementation of enable_if:

// Primary template (OK)
template <bool, typename T>
struct enable_if;

// Specialization for true (also OK)
template <typename T>
struct enable_if<true, T> {
    using type = T;
};

// Specialization for false (Wrong!)
template <typename T>
struct enable_if<false, T> {
    static_assert(std::is_same<T, T*>::value, "No SFINAE here");
    // The condition is always false.
    // Notice also that the condition depends on T but it doesn't make any difference.
};

Then try this

template <typename T>
typename enable_if<std::is_integral<T>::value, int>::type
test(const T &t);

void test(...);

int main()
{
    std::cout << std::is_same<decltype(test(0)), int>::value << std::endl; // OK
    std::cout << std::is_same<decltype(test(0.0)), void>::value << std::endl; // Error: No SFINAE Here
}

If you remove the specialization of enable_if for false then the code compiles and outputs

1
1
like image 91
Cassio Neri Avatar answered Oct 02 '22 22:10

Cassio Neri


On gcc 4.7, compilation fails. I am pretty positive though that this will compile ok in gcc 4.8 (but cannot check at this very moment). Who is right?

The condition in your static assertion does not depend on any template parameter. Therefore, the compiler can immediately evaluate it to false when parsing the template, and realize that the assertion should fire - no matter whether you actually instantiate the template anywhere else.

The same should be true on any compiler.

like image 27
Andy Prowl Avatar answered Oct 02 '22 22:10

Andy Prowl