简体   繁体   中英

Overload resolution for two functions taking parameter packs

Please consider the following two overloads:

template<typename T, typename ...Args>
void f(std::vector<T>&& v, Args&& ...args)
{
    std::cout << "f taking a vector + parameter pack\n";
}

template<typename ...Args>
void f(Args&& ...args)
{
    std::cout << "f taking a parameter pack\n";
}

Now, for the following fragment the expected overload is chosen:

std::vector<int> v{1, 2, 3};
f(std::move(v), 3.0);

(outputs: f taking a vector + parameter pack )

For the following case, the second overload is chosen:

std::vector<int> v{1, 2, 3};
f(v, 3.0);

(outputs: f taking a parameter pack )

The universal reference vector parameter binds to an lvalue reference as well , so why is it that the overload with just the parameter pack is being favored in this case? Update: see the accepted answer, the assumption that the vector parameter is a universal/forwarding reference is wrong.

The universal reference vector parameter binds to an lvalue reference as well

No, it's not a universal reference .

4) If P is an rvalue reference to a cv-unqualified template parameter (so-called "forwarding reference"), and the corresponding function call argument is an lvalue, the type lvalue reference to A is used in place of A for deduction (Note: this is the basis for the action of std::forward Note: in class template deduction , template parameter of a class template is never a forwarding reference (since C++17)):

So for

template<typename T, typename ...Args>
void f(std::vector<T>&& v, Args&& ...args)

note that v is not a rvalue reference to the template paremeter T , but std::vector<T> . So v is "just" an rvalue reference and then can't be bound to an lvalue.

As explained by the accepted answer:

the vector is not considered as Forwarding Reference (aka Universal Reference).

(Compiler knows that it is a vector, thus no template reference deduction for this part, which means that the double ampersand -- && -- is treated as rvalue reference to a vector of unknown type T).

But if we do want to treat a vector as Forwarding Reference?

In case you do wish to pass a vector as a Forwarding Reference you can do something like that:

template<typename T> struct is_vector : public std::false_type {};

template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};

// [1] the special case
// the vector becomes now just V and acts as Forwarding Reference
// the enable_if ensures that we deal with V that is a vector
template<typename V, typename ...Args>
typename std::enable_if<
    is_vector<std::remove_const_t<std::remove_reference_t<V>>>::value
>::type
f(V&& v, Args&& ...args) {
    std::cout << "f taking a vector + parameter pack\n";
}

// [2] the generic case
template<typename ...Args>
void f(Args&& ...args) {
    std::cout << "f taking a parameter pack\n";
}

int main() {
    // all cases below go to the 1st overload
    std::vector<int> v1{1, 2, 3};
    f(v1, 3);

    const std::vector<int> v2{1, 2, 3};
    f(v2, 3);

    f(std::vector<int>{1, 2, 3}, 3);

    // this goes to the 2nd overload
    std::array<int, 3> a{1, 2, 3};
    f(a, 3);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM