考虑以下设想的模板化数组定义示例:
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>
答案 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;
}