【问题标题】:Function Template Overload Resolution & Compiler Optimizations函数模板重载解决和编译器优化
【发布时间】:2017-06-13 02:23:00
【问题描述】:

我正在查看这里发现的这个问题Template function overload for type containing a type

OP user2079802 为他/她的问题提供了此代码:

我正在尝试执行以下操作:

#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
}

在 C++14 中最简单的方法是什么?我以为我可以通过这种方式进行模式匹配,但编译器不会有它。

songyuanyao 提供了这个答案:

模板参数T用作模板名称,因此应声明为template template parameter。例如

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

LIVE


提供的答案确实修复了编译错误并且代码确实运行正常。为了清楚起见,我正在询问有关此代码 sn-p 的问题。 OP 最初试图对模板类型进行模式匹配,但模板模板参数的语法不正确。当我通过在 64 位 Intel Windows 7 机器上运行的 IDE、编译器和调试器 {MSVS 2017 CE} 运行答案时,我碰巧注意到在 OP 的函数调用中的主函数:

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

第二个函数调用实际上是调用第一个函数模板而不是第二个。这确实提出了几个问题:

  • 这是由于编译器优化造成的吗?
  • 这是重载解决的结果吗?
  • 当编译器运行时,编译器内部实际发生了什么 是否选择使用第一个函数模板而不是第二个?
  • 或者这是 MSVC 编译器的错误?

【问题讨论】:

  • 呃,你得到输出“1 2”了吗?还是您从调试器中看到的“第二个函数调用实际上是在调用第一个函数模板”得出这样的结论?
  • @DanielJour 我得到了1 而不是2 的打印值,所以它调用的是第一个函数而不是第二个。
  • 这是MSVC编译器的一个bug。
  • @n.m.检查我的答案,看看它实际上不是 MSVC 中的错误(还)

标签: c++ templates compiler-optimization overload-resolution


【解决方案1】:

这实际上不是 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 demogcc democlang demo

Here's the original defect report (CWG 150)

P0522R0 有最新的讨论,截至 2016 年 11 月,他们建议您在宋元瑶的答案中引用的部分模板匹配的那种根据标准是正确的。

P0522R0 中提出的更改正在被纳入 C++17 标准(我检查过的是 N4296 草案)。在标准最终确定并且 MSVC 声称拥有完整的 C++17 支持之前,我不会将其称为编译器中的错误。目前,他们承认,截至 VS 2017.3 [P2] (source),此特定提案尚未纳入他们的编译器中。

【讨论】:

  • 这看起来像是一个稍微不同的问题。报告中的示例被三大编译器拒绝,而 OP 的代码在 g++ 和 clang++ 中按预期工作。您提出的解决方案没有抓住重点。使用参数包的意义在于我们不想知道或关心可能存在哪些其他模板参数。您的解决方案迫使程序员知道T 接受两个参数,第二个是std::allocator&lt;...&gt;(我们可以写template&lt;class,class&gt;class T,而不是在C++03 中也可以)。幸运的是,fix 很容易。
  • @n.m. std::allocator 的显式使用是出于演示目的:-)。至于它是与报告不同的问题,我倾向于(恭敬地)不同意。 OP 代码在使用 C++14 选项 demo 的 gcc 中无法按预期工作,而使用 C++17 选项 demo 可以正常工作
  • 所以行为在 gcc 6 和 7 之间发生了变化(我只检查了 gcc 6)。
  • 谢谢您的回答;它确实提供了一些很好的见解,并且文件的资源很有帮助。在决定是否接受您的回答之前,我将暂时保留这个问题,看看是否提供了其他答案。
  • 当我检查所有三个编译器的实时代码时;所有三个都打印出2,并为std::allocator&lt;&gt;添加了param,当我从函数定义中删除2ndparam并运行gccclang这三个不同的编译器时仍然打印输出2,其中msvc 打印出1。因此,据我了解,这是每个编译器尝试解析源代码的方式,并且文档确实显示了有关新标准template template parameter 要求的提案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-11
  • 1970-01-01
相关资源
最近更新 更多