C++ 具有专门构造函数的模板类

C++ 具有专门构造函数的模板类,c++,templates,constructor,metaprogramming,C++,Templates,Constructor,Metaprogramming,考虑以下模板化数组定义的人为示例: template <typename t, unsigned int n> class TBase { protected: t m_Data[n]; //... }; template <typename t, unsigned int n> class TDerived : public TBase<t, n> { TDerived() { } }; 模板类TBase { 受

考虑以下模板化数组定义的人为示例:

template <typename t, unsigned int n> class TBase
{
protected:
    t m_Data[n];

    //...
};

template <typename t, unsigned int n> class TDerived : public TBase<t, n>
{
    TDerived()
    {
    }
};
模板类TBase
{
受保护的:
t m_数据[n];
//...
};
模板类TDerived:公共TBase
{
t派生的()
{
}
};
我可以专门化此类型,为长度为2的数组提供非默认构造函数,如下所示:

template <typename t> class TDerived<t, 2> : public TBase<t, 2>
{
public:
    TDerived(const t& x0, const t& x1)
    {
        m_Data[0] = x0;
        m_Data[1] = x1;
    }
};

int main()
{
    TDerived<float, 2> Array2D_A(2.0f, 3.0f); //uses specialised constructor
    TDerived<float, 3> Array3D_A;             //uses default constructor

    return 0;
}
模板类TDerived:公共TBase
{
公众:
t推导(常数t&x0、常数t&x1)
{
m_数据[0]=x0;
m_数据[1]=x1;
}
};
int main()
{
t派生阵列2d_A(2.0f,3.0f);//使用专门的构造函数
TDerived Array3D_A;//使用默认构造函数
返回0;
}
是否有其他方法可以创建一个类,该类在编译时根据模板参数约束不同的构造函数选项,而不需要对每个变体进行完整的类专门化


换句话说,我是否有办法在
TBase
类中拥有专业的构造函数,而不需要中间步骤创建
TDerived
,同时保留
TBase
的功能?

我认为从基类派生类与这里的问题无关,这仅仅是一个实现细节。您真正想要的是,是否有一种方法可以部分地专门化成员函数,比如构造函数。你想要这样的吗

template <typename T, int N> class Foo
{
    Foo(); // general
    template <typename U> Foo<U, 2>(); // specialized, NOT REAL CODE
};
模板类Foo
{
Foo();//一般
模板Foo();//专门化的,不是真正的代码
};
这不管用。你总是要专门化整个班级。原因很简单:在知道存在哪些成员函数之前,必须先知道类的完整类型。考虑下面的简单情况:

template <typename  T> class Bar
{
  void somefunction(const T&);
};

template <> class Bar<int>
{
  double baz(char, int);
};
模板类栏
{
空函数(常数T&);
};
模板类栏
{
双baz(char,int);
};
现在
Bar::somefunction()
依赖于
T
,但该函数仅在
T
不是
int
时存在,因为
Bar
是一个完全不同的类

或甚至考虑另一个专业化>代码>模板类栏:公共zip {};<代码>--即使是一个类的多态性质在一个专门化中也可能完全不同


因此,提供专门化新成员声明(包括构造函数)的唯一方法是专门化整个类。(您可以专门定义现有函数,请参见@Alf的答案。)

对于这一点,我基本上有两种选择:

  • 使用变量函数进行构造(即“…”符号),可以使用该函数中的值n从堆栈中获取参数。但是,如果用户提供了正确数量的参数,编译器将不会在编译时进行检查

  • template<typename T, unsigned int n>
    struct Foo {
      Foo(...) { 
        va_list ap;
        va_start(ap, n);
        for(int j=0; j < n; ++j)
          bork[j] = va_arg(ap, T);
        va_end(ap);
      }
    };
    
  • 使用一些严肃的模板魔术来允许调用更改初始化,如下所示:
    vector(2.0f)(3.0f)
    。实际上,您可以构建一些东西,至少可以确保用户不会在这里提供太多参数。不过这个机制要复杂一些,如果你愿意,我可以组装一个例子


您始终可以专门化一个成员,例如

#include <stdio.h>

template< class Type >
struct Foo
{
    void bar() const
    { printf( "Single's bar.\n" ); }
};

template<>
void Foo< double >::bar() const
{ printf( "double's bar.\n" ); }

int main()
{
    Foo<int>().bar();
    Foo<double>().bar();
}
#包括
模板<类类型>
结构Foo
{
空条()常数
{printf(“单间栏。\n”);}
};
模板
void Foo::bar()常量
{printf(“双精度条。\n”);}
int main()
{
Foo().bar();
Foo().bar();
}
但是你需要的是不同的签名,所以这并不是一个直接专门化成员的例子

一种方法是使用一个参数声明一个构造函数,该参数的类型依赖于模板参数

然后你可以根据自己的需要专门化


Cheers&hth.,

由于构造函数是一个函数,您需要完全专门化包含类以解决特定问题。没有出路

但是,函数不能部分专用(在所有编译器中)。因此,假设您知道当
t=int或double
时需要
n=2
,那么下面是一个备选方案

template<>
TDerived<int,2>::TDerived()
{
  //...
}
template<>
TDerived<double,2>::TDerived()
{
  //...
}
模板
TDerived::TDerived()
{
//...
}
模板
TDerived::TDerived()
{
//...
}
等等

[注意:如果您使用MSVC,那么我认为它支持部分专门化;在这种情况下,您可以尝试:

template<typename t>
TDerived<t,2>::TDerived()
{
  //...
}
模板
TDerived::TDerived()
{
//...
}

不过,我对此还不够肯定。]

您可以在非专用类中给出最常见的定义,并在数组长度上给出
static\u assert
(非C++0x的BOOST\u static\u assert)。这可能被认为是一种黑客行为,但它是解决您的问题的简单方法,而且是安全的

template<typename T, unsigned int n>
struct Foo {
  Foo(const T& x) { static_assert(n == 1, "Mooh!"); }
  Foo(const T& x1, const T& x2) { static_assert(n == 2, "Mooh!"); }
};

-1“提供成员专门化(包括构造函数)的唯一方法是专门化整个类。”不正确。@Alf:Hm,好的,如果您有成员模板,您可以单独专门化这些模板。但我的意思是“在类参数上专门化成员”。怎么说最好?你可以专门化会员。例如,请参阅我的答案。:-)我明白了,但那是不同的,因为你没有改变签名,所以你是专门定义的。在我的示例中,您无法专门化
模板栏::somefunction(const int&)
,因为它不存在。这应该是准确的…好吧,我误解你了。通过修改文本,我删除了否决票。:-)嗯,除了它已经被移除,我不明白。嗯,一个重要的提示。在OP的问题中有两个模板参数,所以我们需要知道这两个参数才能将其社会化。我已经在我的回答中记录了这一点(可能是MSVC支持部分专门化)。@Alf P.Steinbach@iammilind虽然这是一个潜在的解决方案,但理想情况下我希望对构造函数进行部分专门化——例如,通过指定长度而不是类型
TBase(const t&x0){}
,部分专业化如下
模板TBase::TBase(const t&x0)template<typename... T, unsigned int n>
Foo<T, n> make_foo(T&&...) {
  // figure out the common_type of the argument list
  // to our Foo object with setters or as a friend straight to the internals
  Foo< std::common_type< T... >::type, sizeof(T) > foo;
  // recursive magic to pick the list apart and assign 
  // ...
  return foo;
}