【问题标题】:Weird overload resolution with variadic function templates使用可变参数函数模板的奇怪重载解决方案
【发布时间】:2015-07-15 23:56:08
【问题描述】:

我有以下代码:

#include <iostream>

template <typename... Args>
void f(int a, int b, Args... args) { 
    std::cout << b << '\n';
    f(a, args...);
}
void f(int, int b) {
    std::cout << b << '\n';     
}

int main() {
  f(1, 2);
  //f(1, 2, 3);
}

f(1, 2) 编译时,f(1, 2, 3) 不编译。从编译器产生的错误消息中,我看到f&lt;&gt; 正在以某种方式被实例化。在实例化中,调用f(a) 并因此出现错误。是什么让编译器在解析调用f(1, 2, 3)的过程中不使用f(int, int)而是尝试实例化f&lt;&gt;(int, int)

【问题讨论】:

  • 重新排序 decls。 IE。把你的重载首先,然后你的模板,然后再试一次。

标签: c++ c++11 language-lawyer variadic-templates overload-resolution


【解决方案1】:

在可变参数函数模板f()中,由于[temp.dep],递归调用中的f是一个依赖名称,强调我的:

在一个表达式中 形式:

postfix-expression ( expression-listopt)

如果后缀表达式是一个unqualified-idunqualified-id表示一个从属名称如果
(1.1) — 表达式列表 中的任何表达式都是包扩展 (14.5.3),

并且,根据 [temp.dep.res],强调我的:

在解析从属名称时,会考虑以下来源的名称:
(1.1) — 在模板的定义点可见的声明。
(1.2) — 来自与函数参数类型相关的命名空间的声明 实例化上下文 (14.6.4.1) 和定义上下文。

只有一个f 声明在template &lt;typename... Args&gt; void f(int, int, Args...) 的定义点可见,这就是它本身。第二点在这里不适用,因为您的所有参数都是ints,并且基本类型没有关联的命名空间。由于无法使用单个参数调用该函数模板,因此会出现编译错误。

解决方案是重构你的代码,使你的基本情况在定义点可见,即:

// this can be just the declaration
void f(int, int ) { /* ... */ } 

template <typename... Args>
void f(int a, int b, Args... args) 
{ 
    std::cout << b << '\n';
    f(a, args...); // now this will call f(int, int) 
                   // if sizeof...(Args) == 1
}

适用 (1.2) 的示例如下:

#include <iostream>

template <typename A, typename... Args>
void f(A a, int b, Args... args) { 
    std::cout << b << '\n';
    f(a, args...);
}

template <typename A>
void f(A a, int b) {
    std::cout << b << '\n';     
}

struct bar {};

int main() {
    //f(1,2,3);     // still doesn't compile, same reasoning
    f(bar{}, 2, 3); // OK. bar is in the global namespace, so declarations
                    // from the global namespace in both instantiation 
                    // and definition context are considered, which includes
                    // the second `f`.
}

【讨论】:

  • 或者,转发声明void f(int,int)
  • 我只是觉得编译器应该先解析原型再解析定义,这样在解析定义时所有的声明都是可见的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-18
相关资源
最近更新 更多