Function Template Overload Resolution & Compiler O

2019-06-28 04:29发布

问题:

I was looking at this question found here Template function overload for type containing a type

Where the OP user2079802 provided this code for his/her question:

I'm trying to do the following:

#include <iostream>
#include <vector>
#include <tuple>

template <typename T>
void f(T t) {
    std::cout << "1" << std::endl;
}

template <typename T, typename V>
void f(T<std::tuple<V>> t) {
    std::cout << "2" << std::endl;
}

int main() {
    f(std::list<double>{}); // should use first template
    f(std::vector<std::tuple<int>>{}); // should use second template
}

What is the simplest way to do this in C++14? I thought that I could sort of pattern match in this way but the compiler won't have it.

And songyuanyao provided this answer:

The template parameter T is used as a template-name, so it should be declared as template template parameter. e.g.

template <template <typename...> class T, typename V>
//        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void f(T<std::tuple<V>> t) {
    std::cout << "2" << std::endl;
}

LIVE


The answer that was provided did in fact fix the compilation errors and the code does run correctly. I'm asking a question about this code snippet for clarity. The OP was originally trying to pattern match the template types but had improper syntax for template template parameters. When I ran the answer through my IDE, Compiler & Debugger {MSVS 2017 CE} running on a 64bit Intel Windows 7 machine I happen to notice that in the OP's function calls in their main function:

f(std::list<double>{});
f(std::vector<std::tuple<int>>{});

That the 2nd function call is in fact calling the 1st function template and not the 2nd. This does raise a couple of questions:

  • Is this happening due to compiler optimizations?
  • Is this a result of overload resolution?
  • What is actually happening under the hood of the compiler when it is choosing to use the first function template over the 2nd?
  • Or is this a bug with MSVC compiler?

回答1:

It's not actually a bug in the MSVC compiler. It's really a result of an ambiguity in the standard concerning default template parameters.

You see, std::vector actually has 2 template arguments: the type, and an allocator.

If you refactor the answer from that question to account for the allocator

template <typename T>
void f(T t) {
    std::cout << "1" << std::endl;
}

template <template <typename...> class T, typename V>
void f(T<std::tuple<V>, std::allocator<std::tuple<V>>> t) {
    std::cout << "2" << std::endl;
}

It will work properly in all compilers: msvc demo, gcc demo, clang demo.

Here's the original defect report (CWG 150)

P0522R0 has the latest discussion as of November 2016, where they propose that the kind of partial template matching you reference in songyuanyao's answer will be correct according to the standard.

The changes proposed in P0522R0 are being incorporated into the C++17 standard (draft N4296 was the one I checked). Until the standard is finalized and MSVC claims to have full C++17 support, I wouldn't call it a bug in the compiler. For now, they acknowledge that this specific proposal has not yet been incorporated into their compiler as of VS 2017.3 [P2] (source)