Search code examples
c++templatesforwarding-reference

Parameter type check for forwarding reference is not working


I am trying to write a template function that will only accept std::vector as argument. However what I have come up with works only for normal or lvalue parameters, but not for forwarding reference arguments.

The following code doesn't compile.

template <typename T, typename = typename std::enable_if< std::is_same<T, std::vector< typename T::value_type, typename T::allocator_type > >::value>::type >
int f(T && a) {
    return a.size();
}

int main() {
    std::vector<int> a;
    f(a);
}

However if I replace parameter type of f to be an lvalue reference or pass by value type as in:

template <typename T, typename = typename std::enable_if< std::is_same<T, std::vector< typename T::value_type, typename T::allocator_type > >::value>::type >
int f(T a) {
    return a.size();
}

than it compiles.

Note that, I need to have argument of f a forwarding reference.

What am I missing?


Solution

  • If you want to only accept vectors, then your code can be simplified quite a bit. Instead of using SFINAE, you can just specify you want a std::vector with any type of a parameters. That would look like

    template <typename... Pack>
    int f(std::vector<Pack...>const & a)
    {
        return a.size();
    }
    

    The reason your SFINAE approach doesnt work is that when you pass an lvalue to the function T is deduced as a reference type. Reference types don't work like the type itself when it comes to doing things like

    typename T::value_type
    

    To fix that you need to remove the reference-ness of T using std::remove_reference. That would give you a function like

    template <typename T, typename VecType = typename std::remove_reference<T>::type, typename = typename std::enable_if< std::is_same<VecType, std::vector< typename VecType::value_type, typename VecType::allocator_type > >::value>::type >
    int f(T && a) {
        return a.size();
    }