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

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

这作为 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 都不接受我的代码。

是不是跟我对C++模板推导阶段不了解有关?长达 1400 页的规范实际上对我的问题有明确的答案吗?

更新

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

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

int main() {
    f();
}

编译器拒绝你的代码是正确的,它确实是无效的 C++14。

在标准中(这里使用 N4141)我们有

For a generic lambda, the closure type has a public inline function call operator member template (14.5.2) whose template-parameter-list consists of one invented type template- parameter for each occurrence of auto in the lambda’s parameter-declaration-clause, in order of appearance.

(5.1.2/4 [expr.prim.lambda])。所以你的调用相当于调用了一些

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

现在

If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.

(14.8.2/4 [temp.deduct.type]) 和

The non-deduced contexts are:
[...]
- A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.

(14.8.2/5 [temp.deduct.type]) 拨打您的电话 ill-formed.

我无法引用规范,但我会引用 cppreference,这是一个权威来源,通常更容易 read/follow。具体请参见 http://en.cppreference.com/w/cpp/language/template_argument_deduction

Non-deduced contexts

In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction...

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

4) A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done:

其中给出了以下示例:

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);

可变参数 lambda 基本上等同于使用类型模板参数代替 auto 的每次使用的函数模板,因此此示例(无法编译)等同于您的示例。

所以基本上无法推导出第二种类型,因为它是 non-deduced 上下文。

通过提供一个很好的例子来说明为什么决定将其作为 non-deduced 上下文,这个答案可能会得到改进,因为天真地这似乎是可能的。我的猜测是,这基本上是因为函数模板就是一个用于创建函数的模板。反过来,默认参数实质上为同一函数创建多个可调用签名。所以你不能真正处理默认,直到你有一个函数,但是你不能有一个函数,直到你实例化,这需要知道模板参数。

值得注意的是,这个更简单的示例也存在相同的问题:

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

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