Search code examples
c++perfect-forwardingtemplate-templates

Is it possible to perfect forward a template template parameter


I know how to perfect forward a parameter. However, I read from different sources (e.g. Effective Modern C++ Item 24 - Scott Meyers) that one can only perfect-forward when you have the exact template name, e.g.:

template<typename T>
void foo(T&& param) { bar(std::forward<T>(param)); }

What I am looking for is if there is a way to perfect forward an template template parameter, e.g.:

template<template<int, class TypeT> class Vector, int Size, typename TypeT>
void foo(Vector<Size, TypeT>&& param) { bar(std::forward<Vector<Size, TypeT>>(param)); }

When I compile the above, I get an error message: "You cannot bind an lvalue to an rvalue reference" (VC12), which suggest to me that the compiler does not recognize the && as a "universal reference" but as an rvalue reference. This perfect fowarding could be useful to me because I can take advantage of the deduced TypeT and Size.

The question: is it possible to perfect forward template template parameters? If so, where is my syntax incorrect?

Thanks!


Solution

  • A "universal reference" (the standard term is forwarding reference) is (by definition) an rvalue reference to a cv-unqualified template parameter, i.e., T&&.

    Vector<Size, TypeT>&& is an rvalue reference, not a forwarding reference.

    If you want to get the value of the template arguments, write a trait:

    template<class> struct vector_traits;
    
    template<template<int, class TypeT> class Vector, int Size, typename TypeT>
    struct vector_traits<Vector<Size, TypeT>>{
        static constexpr int size = Size;
        using value_type = TypeT;
    };
    

    And inspect std::decay_t<T>:

    template<class T>
    void foo(T&& t) {
        using TypeT = typename vector_traits<std::decay_t<T>>::value_type;
        // use TypeT.
    }
    

    You can also move it to a default template argument, which makes foo SFINAE-friendly (it's removed from the overload set if std::decay_t<T> isn't a "vector"):

    template<class T,
             class TypeT = typename vector_traits<std::decay_t<T>>::value_type>
    void foo(T&& t) {
        // use TypeT.
    }