使用可变参数函数模板进行奇怪的重载分辨率

时间:2015-05-06 03:01:33

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

我有以下代码:

#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<>正在以某种方式实例化。在实例化中,调用f(a),从而产生错误。是什么让编译器不使用f(int, int)但尝试在解析调用f<>(int, int)的过程中实例化f(1, 2, 3)

1 个答案:

答案 0 :(得分:3)

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

  

在表达中   形式:

postfix-expression ( expression-listopt)
     

postfix-expression unqualified-id unqualified-id 表示依赖名称,如果
  (1.1) - 表达式列表中的任何表达式都是包扩展(14.5.3),

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

  

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

只有一个f声明在template <typename... Args> void f(int, int, Args...)的定义中可见,而这本身就是int。第二点不适用于此,因为您的所有参数都是// 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 } ,并且基本类型没有关联的命名空间。由于无法使用单个参数调用该函数模板,因此会出现编译错误。

解决方案是重构代码,以便在定义时可见基本情况,即:

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

(1.2)适用的例子如下:

<a href="http://mypage/comment/reply/NID#comment-form">Reply</a>