Pregunta

I tried to implement a simple tuple according to c++11 variadic templates feature like that:

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

And tried to use it as follows:

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

This resulted in:

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:\Users\achernyaev\Documents\test.cpp: In function 'int main()':
c:\Users\achernyaev\Documents\test.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;
                              ^

with MinGW's g++ 4.8.1

Question: Is this code really malformed or maybe this feature isn't supported good enough yet?

Best regards, Alexander.

¿Fue útil?

Solución

There are several mistake in the code: Here is a corrected one:

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

Otros consejos

Deduction fails for your converting constructor:

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

but succeeds when defined as:

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

I believe deduction fails when trying to match the parameter pack Values up with the non-pack parameter Head of the tuple template, per C++11 §14.8.2.5 [temp.deduct.type]/10:

If the parameter-declaration corresponding to Pi is a function parameter pack, then the type of its declarator-id is compared with each remaining parameter type in the parameter-type-list of A. Each comparison deduces template arguments for subsequent positions in the template parameter packs expanded by the function parameter pack. During partial ordering (14.8.2.4), if Ai was originally a function parameter pack:

  • if P does not contain a function parameter type corresponding to Ai then Ai is ignored;

  • otherwise, if Pi is not a function parameter pack, template argument deduction fails.

In any case, you could simplify the class by making the empty tuple the base case for the recursion, and defining the whole shebang as:

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;
};
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top