专用于模板类构造函数

specialize a template class constructor

本文关键字:构造函数 用于 专用      更新时间:2023-10-16

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

如果类型为 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_;
}

它在Visual Studio 2013中工作得很好。

但是 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){}
   ^

我的代码是错的吗?如果是这样,为什么Visual Studio编译它?或者也许这是一个GCC错误?!

您的代码不正确。顶级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) { }

请注意,is_integralis_floating_point是互斥的,您只需检查其中一个。

在此示例中,将默认值外包给另一个结构可能会简单得多,这样您就可以只有一个如下所示的构造函数:

Foo(T value1 = FooDefaults<T>::value1, T value2 = FooDefaults<T>::value2)
: value1_(value1), value2_(value2)
{ }