函数模板重载解决方案和编译器优化
Function Template Overload Resolution & Compiler Optimizations
我正在看这里发现的这个问题
OP user2079802 为 his/her 问题提供了此代码:
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.
并且 songyuanyao 提供了这个答案:
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;
}
所提供的答案实际上修复了编译错误并且代码 运行 正确。为了清楚起见,我问了一个关于这个代码片段的问题。 OP 最初试图对模板类型进行模式匹配,但模板模板参数的语法不正确。当我 运行 通过我的 IDE、编译器和调试器 {MSVS 2017 CE} 运行 在 64 位英特尔 Windows 7 机器上运行时,我碰巧注意到在 OP 中在其主要功能中调用功能:
f(std::list<double>{});
f(std::vector<std::tuple<int>>{});
第二个函数调用实际上是在调用第一个函数模板而不是第二个。这确实提出了几个问题:
- 这是编译器优化造成的吗?
- 这是重载解析的结果吗?
- 编译器的底层实际发生了什么
正在选择使用第一个功能模板而不是第二个?
- 或者这是 MSVC 编译器的错误?
这实际上不是 MSVC 编译器中的错误。这实际上是标准中有关默认模板参数的歧义的结果。
你看,std::vector
实际上有 2 个模板参数:类型和分配器。
如果您重构该问题的答案以说明分配器
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;
}
它将在所有编译器中正常工作:msvc demo, gcc demo, clang demo。
Here's the original defect report(CWG 150)
P0522R0有最新的讨论截至2016年11月,他们提出根据标准,您在songyuanyao的答案中引用的部分模板匹配是正确的。
P0522R0 中提出的更改正在合并到 C++17 标准中(草案 N4296 是我检查过的)。在标准最终确定并且 MSVC 声称拥有完整的 C++17 支持之前,我不会将其称为编译器中的错误。目前,他们承认这一具体提案尚未纳入他们的编译器,截至 VS 2017.3 [P2] (source)
我正在看这里发现的这个问题
OP user2079802 为 his/her 问题提供了此代码:
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.
并且 songyuanyao 提供了这个答案:
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; }
所提供的答案实际上修复了编译错误并且代码 运行 正确。为了清楚起见,我问了一个关于这个代码片段的问题。 OP 最初试图对模板类型进行模式匹配,但模板模板参数的语法不正确。当我 运行 通过我的 IDE、编译器和调试器 {MSVS 2017 CE} 运行 在 64 位英特尔 Windows 7 机器上运行时,我碰巧注意到在 OP 中在其主要功能中调用功能:
f(std::list<double>{}); f(std::vector<std::tuple<int>>{});
第二个函数调用实际上是在调用第一个函数模板而不是第二个。这确实提出了几个问题:
- 这是编译器优化造成的吗?
- 这是重载解析的结果吗?
- 编译器的底层实际发生了什么 正在选择使用第一个功能模板而不是第二个?
- 或者这是 MSVC 编译器的错误?
这实际上不是 MSVC 编译器中的错误。这实际上是标准中有关默认模板参数的歧义的结果。
你看,std::vector
实际上有 2 个模板参数:类型和分配器。
如果您重构该问题的答案以说明分配器
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;
}
它将在所有编译器中正常工作:msvc demo, gcc demo, clang demo。
Here's the original defect report(CWG 150)
P0522R0有最新的讨论截至2016年11月,他们提出根据标准,您在songyuanyao的答案中引用的部分模板匹配是正确的。
P0522R0 中提出的更改正在合并到 C++17 标准中(草案 N4296 是我检查过的)。在标准最终确定并且 MSVC 声称拥有完整的 C++17 支持之前,我不会将其称为编译器中的错误。目前,他们承认这一具体提案尚未纳入他们的编译器,截至 VS 2017.3 [P2] (source)