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

时间:2017-02-15 04:38:24

标签: c++ lambda c++14 language-lawyer default-parameters

这不能用作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();
}

2 个答案:

答案 0 :(得分:8)

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

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

  

对于通用lambda,闭包类型具有公共内联函数调用   运营商成员模板(14.5.2),其template-parameter-list由一个发明的类型模板组成 -   lambda的parameter-declaration-clause中每次出现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 [temp.deduct.type])和

  

未推断的背景是:
  [...]
       - 在具有默认参数的函数参数的参数类型中使用的模板参数   在正在进行参数推断的调用中使用。

(14.8.2 / 5 [temp.deduct.type])使你的电话不正常。

答案 1 :(得分:3)

我无法引用规范,但我会引用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);

变量lambda基本等同于函数模板,其中类型模板参数替换为auto的每次使用,因此这个示例(不编译)等同于您的示例。

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

这个答案可能会通过提供一个非常好的例子来改进,因为它确切地决定使这个成为一个非推断的背景,因为天真地看起来它是可能的。我的猜测是,这基本上是因为函数模板就是一个用于创建函数的模板。反过来,默认的参数基本上为同一个函数创建多个可调用的签名。所以你不能真正处理默认,直到你有一个函数,但是在实例化之前你不能有一个函数,这需要知道模板参数。

值得注意的是,这个更简单的示例具有相同的问题:

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

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