可变模板在当前编译器中是否能正常工作?

Do variadic templates work correctly with current compilers?

本文关键字:常工作 工作 是否 编译器      更新时间:2023-10-16

我尝试按照c++11可变模板的特性实现一个简单的元组,如下所示:

template <class Head, class... Tail>
class tuple;
template <class Head>
class tuple<Head>
{
public:
    tuple(Head h) : m_h( h ) {}
    tuple(tuple const & rhs)
    : m_h( rhs.m_h ) {}
    template<class T>
    tuple(tuple<T> const & rhs)
    : m_h( rhs.head() )
    {}
    Head head() const
    {
        return m_h;
    }
private:
    Head m_h;
};
template <class Head, class... Tail>
class tuple : private tuple<Tail...>
{
public:
    typedef tuple<Tail...> inherited;
    tuple(Head h, Tail... tail)
    : inherited(tail...), m_h( h )
    {}
    Head head() const
    {
        return m_h;
    }
    inherited &
    tail()
    {
        return *this;
    }
    inherited const &
    tail() const
    {
        return *this;
    }
    template<typename... Values>
    tuple(tuple<Values...> const & rhs)
    : inherited( rhs.tail() ),
      m_h( rhs.head() )
167:    {}
private:
    Head m_h;
};

并尝试如下使用:

    tuple<int, double, char> tpl(0, 3.3, 'a');
175:    tuple<long, float, short> tpl2 = tpl;

结果是:

test.cpp(167) : error C2664: 'tuple<short,>::tuple(const tuple<short,> &)' : can not convert argument 1 from 'const tuple<char,>' to 'short'
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
test.cpp(167) : see reference to function template instantiation 'tuple<float,short>::tuple<double,char>(const tuple<double,char> &)' being compiled
test.cpp(167) : see reference to function template instantiation 'tuple<float,short>::tuple<double,char>(const tuple<double,char> &)' being compiled
test.cpp(175) : see reference to function template instantiation 'tuple<long,float,short>::tuple<int,double,char>(const tuple<int,double,char> &)' being compiled
test.cpp(175) : see reference to function template instantiation 'tuple<long,float,short>::tuple<int,double,char>(const tuple<int,double,char> &)' being compiled

with Visual Studio 2013 and in:

c:UsersachernyaevDocumentstest.cpp: In function 'int main()':
c:UsersachernyaevDocumentstest.cpp:175:35: error: conversion from 'tuple<int,double, char>' to non-scalar type 'tuple<long int, float, short int>' requested tuple<long,float,short> tpl2 = tpl;
                              ^

使用MinGW的g++ 4.8.1

问题:这段代码是否真的有问题,或者这个特性还没有得到足够的支持?

致以最诚挚的问候,Alexander。

代码中有几个错误:下面是更正的:

template <class ...> class tuple; // declaration as a multivariate template.
template <class Head>
class tuple<Head>
{
private:
    Head m_h; 
public:
    tuple(Head h) : m_h( h ) {}
    tuple(tuple const & rhs)
    : m_h( rhs.m_h ) {}
    template<class T>
    tuple(tuple<T> const & rhs)
    : m_h( rhs.head() )
    {}
    Head head() const
    {
        return m_h;
    }
};
template <class Head, class... Tail>
class tuple<Head, Tail...> : // explicitly write the partial specialization. 
   private tuple<Tail...> 
{
private:
    Head m_h;
    typedef tuple<Tail...> inherited;
    tuple(Head h, Tail... tail)
    : inherited(tail...), m_h( h )
    {}
    Head head() const
    {
        return m_h;
    }
    inherited &
    tail()
    {
        return *this;
    }
    inherited const &
    tail() const
    {
        return *this;
    }
    template<typename... Values>
    tuple(tuple<Values...> const & rhs)
    : inherited( rhs.tail() ),
      m_h( rhs.head() )    {}
};

转换构造函数的演绎失败:

template<typename... Values>
tuple(tuple<Values...> const & rhs)
: inherited( rhs.tail() ),
  m_h( rhs.head() )
{}

但是当定义为

时成功
template<typename H, typename... T>
tuple(tuple<H, T...> const & rhs)
: inherited( rhs.tail() ),
  m_h( rhs.head() )
{}

我认为当尝试将参数pack Valuestuple模板的非pack参数Head匹配时,推导失败,根据c++ 11§14.8.2.5 [temp. deduction .type]/10:

如果p i对应的形参声明是一个函数形参包,则将其declarator-id的类型与a的形参类型列表中剩余的每个形参类型进行比较。每次比较都会推导出由函数形参包展开的模板形参包中后续位置的模板实参。在部分排序(14.8.2.4)中,如果Ai最初是一个函数参数包:

  • 如果p不包含对应于a i的函数参数类型,则忽略a i;

  • 否则,如果p i不是函数形参包,则模板实参推导失败。

在任何情况下,您都可以通过将空元组作为递归的基本情况来简化类,并将整个组合定义为:
template <class...> class tuple {};
template <class Head, class... Tail>
class tuple<Head, Tail...> : private tuple<Tail...>
{
public:
    typedef tuple<Tail...> inherited;
    
    tuple(Head h, Tail... tail)
    : inherited(tail...), m_h( h )
    {}
    Head head() const
    {
        return m_h;
    }
    inherited &
    tail()
    {
        return *this;
    }
    inherited const &
    tail() const
    {
        return *this;
    }
    template<typename... T>
    tuple(tuple<T...> const & rhs)
    : inherited( rhs.tail() ),
      m_h( rhs.head() )
    {}
private:
    Head m_h;
};