为什么未选择模板专业化?

时间:2017-07-01 07:17:43

标签: c++ c++11 sfinae

我写了以下代码:

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

当我运行它时,我得到0 0。但我期待0 1

有什么想法吗?

1 个答案:

答案 0 :(得分:21)

对于std::string,选择主模板并考虑专业化。但decltype(++std::declval<T&>())格式不正确,因此不予考虑,并使用主要模板(非专业模板),从而产生0

如果您使用int,则会更复杂一些。主要模板由编译器一如既往地选择,然后考虑专门化(这是因为专门化总是被认为是更好的匹配)。 <int, int&>的专业化为int,但它与非专业化模板<int, void>不匹配(void是默认模板参数),因此忽略了专门化因为它不匹配。

因此,默认模板参数的类型必须匹配,否则不考虑特化,因为只有当每个模板参数与特化时匹配时才会进行特化。

只需在末尾添加void()即可为第二个模板参数进行特化匹配,因为左表达式被丢弃,void()的类型为void,与之匹配主模板的第二个模板参数。

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

在C ++ 17中,您可以使用std::void_t