专门化模板类构造函数

LxL*_*LxL 3 c++ templates class sfinae c++11

我想专门化一个模板类构造函数:

如果type为int,则默认值为50-50.如果它的浮动默认值应为0.5-0.5.

我的代码是:

#include <iostream>
#include <limits>
#include <type_traits>

template<typename T>
class Foo{
public:
    template<typename = typename std::enable_if<
        std::is_integral<T>::value&& !std::is_floating_point<T>::value>::type>
        Foo(T value1 = 50, T value2 = -50) :value1_(value1), value2_(value2){}

    template<typename = typename std::enable_if<
        std::is_floating_point<T>::value>::type>
        Foo(T value1 = 0.5, T value2 = -0.5, void* dummy = 0) : value1_(value1), value2_(value2){}
    T value1_, value2_;
};

int main()
{
    Foo<float> test;
    std::cout << test.value1_ << " " << test.value2_ << '\n';

    Foo<int> test2;
    std::cout << test2.value1_ << " " << test2.value2_;
}
Run Code Online (Sandbox Code Playgroud)

它在2013年的visual studio中运行得很好.

但是gcc 4.9.2拒绝它:

main.cpp: In instantiation of 'class Foo<float>':
main.cpp:29:13:   required from here
main.cpp:19:3: error: no type named 'type' in 'struct std::enable_if<false, void>'
   Foo(T value1 = 50, T value2 = -50) :value1_(value1), value2_(value2){}
   ^
main.cpp: In instantiation of 'class Foo<int>':
main.cpp:32:11:   required from here
main.cpp:23:3: error: no type named 'type' in 'struct std::enable_if<false, void>'
   Foo(T value1 = 0.5, T value2 = -0.5, void* dummy = 0) : value1_(value1), value2_(value2){}
   ^
Run Code Online (Sandbox Code Playgroud)

我的代码错了吗?如果是这样,为什么visual studio会编译它?或者它可能是一个gcc bug?!

Bar*_*rry 7

您的代码不正确.顶级T不能在SFINAE上下文中用于其方法,这正是您要尝试的方法.只有在直接上下文中发生的替换才可能导致扣减失败(§14.8.2/ 8):

只有函数类型的直接上下文中的无效类型和表达式及其模板参数类型才会导致演绎失败.[注意:对替换类型和表达式的评估可能会导致副作用,例如类模板特化和/或函数模板特化的实例化,隐式定义函数的生成等.这些副作用不在"立即上下文"并且可能导致程序格式不正确. - 结束说明]

GCC和Clang拒绝你的代码是正确的.

一种解决方法就是引入一个虚拟的模板类型默认为顶层T,并SFINAE上一个.像这样:

template <typename T_ = T, // now the subsequent line *is* in
                           // an immediate context
          typename = typename std::enable_if <
              std::is_integral<T_>::value&& !std::is_floating_point<T_>::value
          >::type>
Foo(T value1 = 50, T value2 = -50) 
:value1_(value1), value2_(value2) { }
Run Code Online (Sandbox Code Playgroud)

请注意is_integral并且is_floating_point是互斥的,您只需要检查其中一个.

在这个例子中,将默认值分配给另一个结构可能要简单得多,这样你就可以只有一个构造函数,如下所示:

Foo(T value1 = FooDefaults<T>::value1, T value2 = FooDefaults<T>::value2)
: value1_(value1), value2_(value2)
{ }
Run Code Online (Sandbox Code Playgroud)