Ranoiaetep
Ranoiaetep

Reputation: 6637

How are generic template and function overload being chosen?

I was originally trying to write a wrapper class for std::vector. I had a templated constructor, that forwards a parameter pack to imitate different constructors for std::vector. Then I decided to had some specialized/overloaded ones if a std::vector was passed in it.

While writing it, I decided to try how different function overload and template specialization works with.

Here are the codes that I have:

template<typename Type>
struct myVec
{
    template <typename ...Params>
    myVec(Params&&... params)
    :vec(std::forward<Params>(params)...)
    {
        std::cout << "Generic used\n";
    }
    
    template <>
    myVec<std::vector<Type>&&>(std::vector<Type>&& params)
    {
        std::cout << "Specialization used\n";
    }
    
    myVec(std::vector<Type>& params)
    {
        std::cout << "Overload used\n";
    }
    
private:
    std::vector<Type> vec;
};

int main()
{
    std::vector<int> v{1,2,3,4,5};
    myVec<int> vec1(v);                                 // Print "Overload Used"
    myVec<int> vec2(std::vector<int> {1,2,3,4,5});      // Print "Specialization used"
    // myVec<int> vec3(&v);                             // Error, attempting to use generic template, 
                                                        // which attempt to search for std::vector(std::vector& vec), which does not exist
}

So my question would how exactly do they decide which constructors to use? I thought vec1 would use the generic template, and vec3 would use the the overload, but apparently those are not the cases.

Also seems like for my specialization, I could just replace the explicit template <std::vector<Type>&&>to <std::vector<Type>> to <>, or just remove it all together, and they would all work the same for me?

Upvotes: 1

Views: 147

Answers (1)

Hernando Abella
Hernando Abella

Reputation: 326

Firstly, your code does not compile on GCC (I'm guessing you're using VS?) This is because you can't specialize the template for myVec constructor without specializing the whole class. See this: Explicit specialization in non-namespace scope

As you said in the last paragraph of the question, there is no need of the specialization anyways, so you can just remove it. This code works:

#include <iostream>
#include <vector>

template<typename Type>
struct myVec
{
    template <typename ...Params>
    myVec(Params&&... params)
    :vec(std::forward<Params>(params)...)
    {
        std::cout << "Generic used\n";
    }
    
    myVec(std::vector<Type>&& params)
    {
        std::cout << "Specialization used\n";
    }
    
    myVec(std::vector<Type>& params)
    {
        std::cout << "Overload used\n";
    }
    
private:
    std::vector<Type> vec;
};

int main()
{
    std::vector<int> v{1,2,3,4,5};
    myVec<int> vec1(v);                              // Print "Overload Used"
    myVec<int> vec2(std::vector<int> {1,2,3,4,5});    // Print "Specialization used"
    // myVec<int> vec3(&v);                          // Error, attempting to use generic template, 
                                                        // which attempt to search for std::vector(std::vector& vec), which does not exist
}

vec1 uses the overloaded template because, well.... it is a direct match. No other constructors match the argument v. This is because when you pass v to the constructor, it will be passed as an lvalue reference, that is std::vector<int>& (or const vector<int>&). See lvalue references:

https://en.cppreference.com/w/cpp/language/reference

vec2 uses the 'specialized' template because it expects an rvalue reference and std::vector<int>{1, 2, 3, 4, 5} will be passed as an rvalue reference. So it's an exact match.

Upvotes: 1

Related Questions