Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the template specialization not chosen?

Tags:

c++

c++11

sfinae

I wrote the following code:

#include <iostream>
#include <string>
#include <type_traits>

template<typename, typename = void>
struct is_incrementable : std::false_type {};

template<typename T>
struct is_incrementable<T, decltype( ++std::declval<T&>() )> : std::true_type {};

int main()
{
    std::cout << is_incrementable<std::string>::value << std::endl;
    std::cout << is_incrementable<int>::value << std::endl;
}

When I run it, I get 0 0. But I expected 0 1.

Any ideas?

like image 568
ellipsis Avatar asked Jul 01 '17 07:07

ellipsis


People also ask

Why not specialize function templates?

Moral #1: If you want to customize a function base template and want that customization to participate in overload resolution (or, to always be used in the case of exact match), make it a plain old function, not a specialization. And, if you do provide overloads, avoid also providing specializations.

Why does the need of template specialization arise?

It is possible in C++ to get a special behavior for a particular data type. This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming.

What is template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

How will you restrict the template for a specific datatype?

There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.


1 Answers

For std::string, the primary template is chosen and the specialization is considered. But decltype(++std::declval<T&>()) is ill-formed, so it is not considered and the primary template (non-specialized template) is used, which results in 0.

If you use int, it gets a bit more complicated. The primary template gets chosen by the compiler, as always, and then the specialization is considered (that's because specialization are always considered better matches). The specialization is <int, int&> for int, but it doesn't match the non-specialized template <int, void> (the void is the default template argument), so the specialization is ignored because it doesn't match.

So, the types of default template parameter have to match, or else the specialization is not taken into account, as a specialization is only taken when every template argument matches the specialization.

Just append a void() at the end to make the specialization match for the second template parameter, as the left expression is discarded and the type of void() is void, which matches the primary template's second template parameter.

template<typename T>
struct is_incrementable<T, decltype( ++std::declval<T&>(), void() )> : std::true_type {};

In C++17, you would use std::void_t for that.

like image 197
Rakete1111 Avatar answered Sep 19 '22 20:09

Rakete1111