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

时间:2011-08-11 13:12:22

标签: 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()
    {
    }
};

我可以专门使用这种类型为长度为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;
}

还有其他方法可以创建一个类,它在编译时具有不同的模板参数约束的不同构造函数选项,而不需要对每个变体进行完整的类特化吗?

换句话说,是否有某些方法可以在TBase类中拥有专门的构造函数,而无需在保留TDerived的功能的同时创建TBase的中间步骤? / p>

5 个答案:

答案 0 :(得分:2)

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

template <typename T, int N> class Foo
{
    Foo(); // general
    template <typename U> Foo<U, 2>(); // specialized, NOT REAL CODE
};

这不起作用。你总是需要专注于整个班级。原因很简单:您必须先知道之前的类的完整类型,您甚至知道存在哪些成员函数。请考虑以下简单情况:

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

template <> class Bar<int>
{
  double baz(char, int);
};

现在Bar<T>::somefunction()取决于T,但T不是int时仅存在,因为Bar<int>是一个完全不同的阶级。

或者考虑另一个专业化template <> class Bar<double> : public Zip {}; - 甚至一个类的多态性在专业化方面也可能完全不同!

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

答案 1 :(得分:1)

我看到基本上有两种选择:

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

  • 使用一些严肃的模板魔术来允许调用chaning初始化,如下所示:vector(2.0f)(3.0f)。你实际上可以构建一些东西,至少可以确保用户不会在这里提供太多的参数。然而,机制更多涉及,如果你愿意,我可以组装一个例子。

答案 2 :(得分:1)

你总是可以专门化一个成员,例如

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

但是你想要有效地使用不同的签名,所以它不是一个专门化成员的情况。

然后,前进的方法是使用单个参数声明构造函数,该参数的类型取决于模板参数。

然后你可以根据需要专门研究它。

干杯&amp;第h。,

答案 3 :(得分:1)

由于构造函数是一个函数,因此您需要完全专门化包含类以解决您的特定问题。没办法。

但是,函数不能部分专门化(在所有编译器中)。因此,假设您在n = 2时知道您需要t = int or double,那么以下是另一种选择。

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

等等。

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

template<typename t>
TDerived<t,2>::TDerived()
{
  //...
}
但是,我对此并不确定。]

答案 4 :(得分:0)

您可以在非专用类中提供最常见的定义,并在数组长度上提供static_assert(非C ++ 0x的BOOST_STATIC_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!"); }
};

“邪恶”的方式是可变参数。

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

然后还有C ++ 0x和旧的make_something技巧,这比人们想象的要困难。

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