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

时间:2015-09-02 17:17:07

标签: c++ templates language-lawyer function-templates default-arguments

C ++标准第8.3.6.4节说

  

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

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

考虑一下编译好的程序。 (非模板功能)(参见现场演示here.

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

但是编辑失败了。 (模板功能)(参见现场演示here.

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

2 个答案:

答案 0 :(得分:10)

这是一个历史性的限制,它在标准化过程中很早就加入了(它存在于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)

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

委员会因此提出警告,因此决定更普遍地禁止在模板中添加默认参数。

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

答案 1 :(得分:-6)

因为这根本不可能。

为了实现您的目标,编译器必须实现一个函数,给定两个模板函数,它们返回它们是否是相同的函数。问题是这个功能无法实现。

对于常规的非模板函数,这是一个可怕的想法,但仍然可行,因为你只需要匹配参数类型和完成的工作。

不幸的是,对于模板功能,这变得......更加棘手。考虑:

template<typename T> void f(T t);
template<typename U> std::enable_if_t<std::is_same<U, int>::value> f(U u = U());

您可以看到他们可能声明了相同的功能,如果 Tint。否则,他们没有。与默认模板参数和类似的事情有更多有问题的交互,但长话短说,这对模板来说是不可判定的。