Sorry for the title, I am not sure about the category of my question. I am trying to do an is_incrementable with SFINAE. This works fine, however when I try to understand it more deeply, and when I remove the void_t then the code snippet does not work as expected.
The original code:
#include <iostream>
template< typename, typename = void >
struct is_incrementable : std::false_type { };
template< typename T >
struct is_incrementable<T,
std::void_t<decltype( ++std::declval<T&>() )>
> : std::true_type { };
int main()
{
std::cout << is_incrementable<int>::value << std::endl; // prints 1
std::cout << is_incrementable<std::string>::value << std::endl; // prints 0
return 0;
}
i) is_incrementable<int>::value
evaluates to is_incrementable<int, void>::value
which is the original template class and the specialization, too. In this case the compiler choses the specialized version, so value eguals to 1.
For the string version, the specialization fails, SFINAE kicks in, so we have the base template only. (value equals to 0)
ii) When I change the code, and remove the void_t
template< typename, typename = void >
struct is_incrementable : std::false_type { };
template< typename T >
struct is_incrementable<T,
decltype( ++std::declval<T&>() ) // void_t is removed
> : std::true_type { };
int main()
{
std::cout << is_incrementable<int>::value << std::endl; // prints 0
std::cout << is_incrementable<std::string>::value << std::endl; // prints 0
return 0;
}
0 and 0 is printed.
is_incrementable<int>::value
means is_incrementable<int, void>::value
, the specialization is
is_incrementable<int, int>::value
(I think), so we use the basic template. For the string the specialization fails anyway.
My question: iii) The funny part. If now I change the first line to use int as the default type
#include <iostream>
template< typename, typename = int > // !!! int is used now
struct is_incrementable : std::false_type { };
template< typename T >
struct is_incrementable<T,
decltype( ++std::declval<T&>() ) // void_t is removed
> : std::true_type { };
int main()
{
std::cout << is_incrementable<int>::value << std::endl; // prints 0
std::cout << is_incrementable<std::string>::value << std::endl; // prints 0
return 0;
}
then again 0 and 0 are printed.
Why?
is_incrementable<int>::value
means (I think) is_incrementable<int, int>::value
and
decltype( ++std::declval<T&>() )
should be int as well.
So I think the compiler should use the specialized version. (and 1 should be printed)
If I remove
decltype( ++std::declval<T&>() )
and write int
then 1 and 1 printed (which are the expected print outs).
Could someone explain me what is happening in iii) please.