为什么以后在模板函数中不能添加默认参数?

Des*_*tor 15 c++ templates language-lawyer function-templates default-arguments

C++标准第8.3.6.4节说明了这一点

对于非模板函数,可以在稍后的同一范围内的函数声明中添加默认参数.[...]

但我的问题是,为什么它不允许模板功能?不允许在模板函数的同一范围内的后续声明中添加默认参数的基本原理是什么?

考虑这个编译好的程序.(非模板功能)(请参阅此处的现场演示.)

#include <iostream>

int f(int a,int b,int c=3);
int f(int a,int b=9,int c); // default argument in middle, ok allowed

int main()
{
    f(3);
    f(3,6);
    f(3,6,9);
    return 0;
}

int f(int a,int b,int c)
{
    std::cout<<a<<' '<<b<<' '<<c<<'\n';
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是在编译失败之后.(模板功能)(参见此处的现场演示.)

#include <iostream>

template <typename T> 
void f(T a,int b,int c=3);
template <typename T> 
void f(T a,int b=9,int c); // compiler error why???

int main()
{
    f(3);
    f(3,6);
    f(3,6,9);
    return 0;
}

template <typename T> 
void f(T a,int b,int c)
{
    std::cout<<a<<' '<<b<<' '<<c<<'\n';
} 
Run Code Online (Sandbox Code Playgroud)

Dav*_* V. 11

这是在标准化过程中很早就添加的历史限制(它存在于C++ 98中,但不存在于ARM中).

我不记得确切的原因(我的同事也没有,在作出决定时几乎肯定在那里).不过,我猜...

那时,所有一个编译器通过解析重放令牌来实例化模板.一些几乎没有解析过的模板.考虑:

template<class T> struct S {
  T f(T);  // (1)
};
template<class T> T S<T>::f(T p = 42) { return p; }  // (2)
S<int> s;  // Causes the "real" parsing of (1), but not (2).
int r = s.f();  // (3)
Run Code Online (Sandbox Code Playgroud)

在解析call(3)时,较旧的编译器通常只能访问实例化的声明(1),而(2)仍然没有真正解析(只是令牌缓冲).结果,这些编译器不知道(3)中添加的默认参数.

怀疑是否因此委员会决定更普遍地禁止在模板中添加默认参数.

这种限制在今天可能较少(技术上)合理,因为其他标准要求因此导致需要以其通用形式解析模板(但是,例如,MSVC仍然没有这样做AFAICT).也就是说,实现它可能仍然有点痛苦,因为默认参数现在可能必须在各种不同的上下文中实例化.

  • 无法相信这个问题已经由模板专家和C++模板的作者完整指南回答:David Vandevoorde (5认同)
  • 回覆。“令牌缓存” ...一些编译器(MSVC,基于EDG的编译器,3.4之前的GCC以及大多数较早的编译器,如Cfront)通过“缓存”它们组成的令牌来处理模板。然后在实例化时,这些标记通过解析器“重播”,除了模板参数标记被相应的模板参数替换之外。因此,模板可能处于“仅令牌缓冲;未解析”状态。 (2认同)