具有可变参数的C ++模板类构造函数

时间:2010-04-13 05:41:44

标签: c++ templates variadic-functions

是否可以创建一个带有可变数量参数的模板函数,例如,在此Vector< T, C >类构造函数中:

template < typename T, uint C >
Vector< T, C >::Vector( T, ... )
{
    va_list arg_list;
    va_start( arg_list, C );
    for( uint i = 0; i < C; i++ ) {
        m_data[ i ] = va_arg( arg_list, T );
    }
    va_end( arg_list );
}

这几乎可以工作,但如果有人调用Vector< double, 3 >( 1, 1, 1 ),则只有第一个参数具有正确的值。我怀疑第一个参数是正确的,因为它在函数调用期间被强制转换为double,而其他参数被解释为int s然后这些位被填充到double中。调用Vector< double, 3 >( 1.0, 1.0, 1.0 )可获得所需的结果。有没有一种首选的方式来做这样的事情?

8 个答案:

答案 0 :(得分:9)

唉,现在没有好办法做到这一点。大多数需要做类似事情的Boost包使用宏技巧来定义这样的事情:

template < typename T >
Vector< T >::Vector( T )
{ ... }

template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1 )
{ ... }

template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1, C c2 )
{ ... }

template < typename T, uint C >
Vector< T, C >::Vector( T t, C c1, C c2, C c3 )
{ ... }

宏生成一些设定数量(通常约为10个)版本,并提供一种机制来在扩展构造之前更改参数的最大数量。

基本上,这是一个真正的痛苦,这就是为什么C ++ 0x引入了可变长度模板参数和委托方法,可以让你干净(安全)地完成这项工作。在此期间,您可以使用宏来执行此操作,也可以尝试使用支持(部分)这些新实验功能的C ++编译器。 GCC对此很好。

请注意,由于C ++ 0x实际上还没有出现,所以事情仍然会发生变化,而且您的代码可能与标准的最终版本不同步。此外,即使在标准出来之后,也会有5年左右的时间,许多编译器只会部分支持标准,因此您的代码将不会非常便携。

答案 1 :(得分:2)

这段代码看起来很危险,我认为你对它不起作用的原因进行了分析,编译器在调用时无法知道:

Vector< double, 3 >( 1, 1, 1 )

那些应该作为双打传递。

我会将构造函数更改为:

Vector< T, C >::Vector(const T(&data)[C])
而是,让用户将参数作为数组传递。另一种丑陋的解决方案是这样的:

template < typename T, uint C >
Vector< T, C >::Vector(const Vector<T, C - 1>& elements, T extra) {
}

并像这样调用它(使用某些typedef):

Vector3(Vector2(Vector1(1), 1), 1);

答案 2 :(得分:2)

你可以做你想做的事,但不要做,因为它不是类型安全的。最好传递T的向量或包含这些值的一对迭代器。

template < typename T, uint C >
Vector< T, C >::Vector(int N, ... )
{
    assert(N < C && "Overflow!");
    va_list arg_list;
    va_start(arg_list, N);
    for(uint i = 0; i < N; i++) {
        m_data[i] = va_arg(arg_list, T);
    }
    va_end(arg_list);
}

Vector<int> v(3, 1, 2, 3);

这可以更好地解决,因为无论如何所有元素都是同质的。

template < typename Iter, uint C >
Vector< T, C >::Vector(Iter begin, Iter end)
{
    T *data = m_data;
    while(begin != end)
      *data++ = *begin++;
}

int values[] = { 1, 2, 3 };
Vector<int> v(values, values + 3);

当然,您必须确保m_data中有足够的位置。

答案 3 :(得分:0)

答案 4 :(得分:0)

在C ++ 0x中(实际上应该称为C ++ 1x),你可以使用模板变量以类型安全的方式实现你想要的东西(你甚至不需要指定参数的数量!)。但是,在当前版本的C ++(ISO C ++ 1998 with 2003修订版)中,没有办法完成你想要的。您可以推迟或执行Boost所做的操作,即使用preprocessor macro magic使用不同数量的参数重复构造函数的定义,直到硬编码但大的限制。鉴于Boost.Preprocessor有点复杂,你可以自己定义以下所有内容:

Vector<T,C>::Vector();
Vector<T,C>::Vector(const T&);
Vector<T,C>::Vector(const T&, const T&);
// ...

由于上述手工操作很痛苦,但您可以编写一个脚本来生成它。

答案 5 :(得分:0)

std::tr1::array(看起来与你的相似)没有定义构造函数,可以初始化为聚合(?)

std::tr1::array<int, 10> arr = {{ 1, 2, 3, 4, 5, 6 }};

您还可以查看Boost.Assignment库。

例如构造函数可以是

template < typename T, uint C >
template < typename Range >
Vector< T, C >::Vector( const Range& r ) 

和使用

创建的实例
Vector<int, 4> vec(boost::assign::cref_list_of<4>(1)(3)(4)(7));

答案 6 :(得分:0)

您可以使用variadic,variadic表示带有变量参数的模板。more

答案 7 :(得分:0)

构造函数中的变量参数的问题是:

  • 您需要cdecl调用约定(或另一个可以处理varargs的约定)
  • 你不能为构造函数定义cdecl(在MSVS中)

所以“正确”的代码(MS)可能是:

template < typename T, uint C > __cdecl Vector< T, C >::Vector( T, ... )

但编译器会说:

  

构造函数/析构函数的非法调用约定(MS C4166)