Search code examples
c++11variadic-templatesperfect-forwardingvisual-c++-2013

Variadic templates and perfect forwarding to specific template class


I want to implement a perfect forwarding constructor in my "myArgs class", which should only get involved for specializations of myClassBase<>. Roughly speaking: call this constructor for each variation of myClassBase<>

But it won't compile: '<function-style-cast>' : cannot convert from 'myClass' to 'myArgs'.

I think it's because the compiler can't deduce from Args&& to myClassBase<T, D>.

See this (very basic) example:

template <class T, class D>
class myClassBase
{
private:
    T data;
    D data2;
};

typedef myClassBase<char, int> myClass;

class myArgs
{
public:
    myClass m_data;

    template <class T, class D>
    myArgs(myClassBase<T, D>&& rhs) :
        m_data(std::forward< myClassBase<T, D> >(rhs))
    {
    }
};

template <class... Args>
void var_args(Args&&... args)
{
    myArgs( std::forward<Args>(args)... );
}

Test the stuff:

myClass x;

var_args(x);

If i change the var_args function parameter from void var_args(Args&&... args) to void var_args(Args... args) it works.

Btw. in my real code myClass of course supports move semantics.

Thanks in advance. Christian.


Solution

  • template <class T, class D>
    myArgs(myClassBase<T, D>&& rhs) :
        m_data(std::forward< myClassBase<T, D> >(rhs))
    {
    }
    

    rhs there is an rvalue reference, not a forwarding reference. A forwarding reference in a function parameter be of the form T&& where T is some deduced template parameter. You can fix this by having T deduced rather than specifying myClassBase.

    template <class T>
    myArgs(T&& rhs) :
        m_data(std::forward<T>(rhs))
    {
    }
    

    If you want this function to only be valid if T is a myClassBase, you can write a trait to check it:

    template <typename T>
    struct isMyClassBaseImpl : std::false_type{};
    
    template <typename T, typename D>
    struct isMyClassBaseImpl<myClassBase<T,D>> : std::true_type{};
    
    template <typename T>
    using isMyClassBase = isMyClassBaseImpl<std::decay_t<T>>;
    

    Then you can SFINAE it out:

    template <class T, std::enable_if_t<isMyClassBase<T>::value>* = nullptr>
    myArgs(T&& rhs) :
        m_data(std::forward<T>(rhs))
    {
    }