C++14 lambda 的默认参数类型推导,具体取决于前面的参数

C++14 lambda's default argument type deduction depending on preceding arguments

本文关键字:参数 取决于 前面 lambda 默认 类型 C++14      更新时间:2023-10-16

这对C++14无效吗?

auto f = [](auto x, auto y = std::decay_t<decltype(x)>{}) { };
f(0);

我原以为它大致相当于

auto f = [](int x, int y) { };
f(0, int{});

GCC 6.3和Clang 4.0都不接受我的代码。

  • http://ideone.com/b7b4SKGCC
  • http://ideone.com/EyLYaLClang

这与我对C++模板推导阶段缺乏了解有关吗?这份长达1400页的规范真的对我的问题有明确的答案吗?

更新

总之,我的问题实际上可以简化为这段代码(没有lambda,单个参数),并且在C++14下是无效的(感谢@BaummitAugen和@NirFriedman)

template <typename T>
void f(T x = 0) { }
int main() {
f();
}

编译器拒绝您的代码是正确的,它确实不是有效的C++14。

在标准中(此处使用N4141),我们有

对于泛型lambda,闭包类型具有公共内联函数调用操作员成员模板(14.5.2),其模板参数列表由一个发明的类型模板组成-lambda的参数声明子句中每次出现auto的参数,按出现顺序排列。

(5.1.2/4[expr.prim.lambda])。因此,您的调用相当于对某个的调用

template <class T1, class T2>
auto operator() (T1 x, T2 y = std::decay_t<decltype(x)>{});

现在

如果模板参数仅用于非推导上下文,并且没有明确指定,则模板参数推导失败。

(14.8.2/4[临时扣除类型])和

非推导上下文为:
[…]
-在具有默认参数的函数参数的参数类型中使用的模板参数正在进行参数推导的调用中使用的。

(14.8.2/5[temp.dexer.type])会使您的调用格式不正确。

我不能引用规范,但我会引用cppreference,它是一个权威来源,通常更容易阅读/遵循。特别是,请参见http://en.cppreference.com/w/cpp/language/template_argument_deduction.

非推导上下文

在以下情况下,用于组成p的类型、模板和非类型值不参与模板参数推导。。。

您可能已经意识到模板参数不能总是推导出来的。往下看条目列表,我们看到:

4)在函数参数的参数类型中使用的模板参数,该参数具有正在进行参数推导的调用中使用的默认参数:

它给出了以下示例:

template<typename T, typename F>
void f(const std::vector<T>& v, const F& comp = std::less<T>());
std::vector<std::string> v(3);
f(v);

可变lambdas基本上等效于函数模板,每个auto的使用都替换了一个类型模板参数,所以这个示例(不编译)等效于您的示例。

所以基本上,第二种类型是无法推导的,因为它是一个非推导的上下文。

通过给出一个很好的例子,说明为什么决定将其作为一个非推导的上下文,这个答案可能会得到改进,因为天真地认为这是可能的。我的猜测是,这基本上是因为函数模板就是创建函数的模板。反过来,默认参数本质上为同一函数创建多个可调用签名。因此,在拥有函数之前,您无法真正处理默认情况,但在实例化之前,您不能拥有函数,这需要了解模板参数。

值得注意的是,这个更简单的例子有同样的问题:

template<typename T, typename F>
void f(const std::vector<T>& v, const F& comp = int{});

因此,对第一个模板参数的依赖实际上与问题无关。