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?
For
std::string
, the primary template is chosen and the specialization is considered. Butdecltype(++std::declval<T&>())
is ill-formed, so it is not considered and the primary template (non-specialized template) is used, which results in0
.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&>
forint
, but it doesn't match the non-specialized template<int, void>
(thevoid
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 ofvoid()
isvoid
, which matches the primary template's second template parameter.In C++17, you would use
std::void_t
for that.