在研究了#1664的拟议决议后(
proposed resolution 1664
),我对函数模板的默认参数的规则感到困惑,请在这里引用以下内容:
根据第8.1.5款【解释原始lambda】第3段
闭包类型在包含
对应的lambda表达式。[注意:这决定了与闭包类型(6.4.2[basic.lookup.argdep])关联的名称空间和类的集合。lambda的参数类型-
声明器不会影响这些关联的名称空间和类。-结束语]
然而,17.8.1【临时安装】第13段中
如果函数模板f的调用方式要求使用默认参数,则会查找依赖名称,检查语义约束,并对默认参数中使用的任何模板进行实例化,就像默认参数是在具有相同作用域的函数模板专用化中使用的初始值设定项一样,与当时使用的函数模板f相同的模板参数和访问权限。
因此,一种可能性是,在模板函数(或者,可能是类模板的成员函数)的默认参数中,lambda表达式的闭包类型被视为已在虚构函数模板专门化的主体中的某个块范围内声明。
考虑下面的例子:
namespace J {
inline namespace K {
template <typename T> int zap(const T &t) { foo(t); return 0; }
template <typename T> void zip(int = zap([] { })) { }
}
template <typename T> void foo(const T &) { }
}
void bar() {
J::K::zip<long>();
/*Accroding to the above wording,the invoke just like:
=> J::K::zip<long>(zap([] { }));
*/
}
如果zip不是一个模板,那么依赖参数的查找在所有测试的实现中都成功地解析了foo的查找;然而,在编写的示例处理过程中存在实现差异。
拟议决议(2013年9月):
将17.8.1[温度说明]第13段更改如下:
如果函数模板f的调用方式要求使用默认参数,则会查找依赖名称,检查语义约束,并对默认参数中使用的任何模板进行实例化,就像默认参数是在具有相同作用域的函数模板专用化中使用的初始值设定项一样,与当时使用的函数模板f相同的模板参数和访问权限,
除了声明闭包类型的范围(8.1.5[expr.prim.lambda])以及与其相关的名称空间仍然是根据默认参数定义的上下文确定的
.这种分析称为默认参数实例化。然后将实例化的默认参数用作f的参数。
注意强调的部分,如果我没有误解,这意味着如果强调的部分注释掉,那么
foo
无法通过
依赖于参数的查找
因为争论
[] { }
哪个名称空间既不是
J
也没有
K
,采用
function bar
喜欢
J::K::zip<long>(zap([] { }) /*default argument*/);
,所以根据
[expr.prim.lambda]第3段
的名称空间
[] { }
在
fuction bar
在这个范围内,没有
福
可以找到,因此强调的部分是考虑这个名称空间的
[] { }
在内部
zap
一如
扎普
,它意味着
[] { }
是
K
现在
福
可以在父命名空间中找到
J
根据依赖于参数的查找规则,到目前为止,如果我误解了这些规则,请纠正我。另一种观点是,每次调用函数时都会计算默认参数,即使默认参数是
不依赖
所以继续考虑下面的代码:
#include <iostream>
struct A {
};
template<typename T>
int func(T, float) { //#a
std::cout << "float" << std::endl;
return 0;
}
template<typename T>
void test(int = func(A{}, 0)) { //#1
}
template<typename T>
int func(T, int) { //#b
std::cout << "int" << std::endl;
return 0;
}
int main() {
test<A>(); //#2 transform to: test<A>(func(A{}, 0)); here,#b should be the best match
std::getchar();
}
虽然默认参数
func
是非依赖的,但是每次函数
test
调用,我在一些编译器中测试代码。
所有版本的MSVC报告“int”,gcc报告“float”,铿锵报告“float”,到底是什么?根据gcc或clang的报告,似乎
func
#1
MSVC证明
func
决定于
#2
.如果MSVC是错误的,这意味着非依赖默认参数可以在#1内确定,并且无需确定每次调用函数的时间,为什么需要添加强调部分?(
如果我正确理解了强调的部分,那么它的目的是在默认参数内保持闭包类型的名称空间一致,无论lambda表达式是在函数声明点还是在调用点
).如果我误解了这些规则,如何正确解释它们?
更新:
gcc的9.1或更高版本无法完成#1664中提到的代码,它将报告错误(
the complie result
)
问题:
1.每次调用相应函数时,是否需要确定函数模板或非模板函数的非依赖默认参数?
2.“默认参数的定义”是什么意思?这个措辞严格吗?(
换句话说,我的理解是,实际上添加的规则想要表达的是closeure类型的名称空间是一个函数declaration的名称空间,其中包含一个包含相应lambda表达式的默认参数,对吗?如果我对此的理解是错误的,请纠正我
)