33

我认为以下代码非常方便且无害:

auto fn = [](bool b = false) -> int // NOT legal in C++11
{
    return b ? 1 : 0;
}; 

为什么 C++11 明确禁止 lambda 表达式的默认参数?

我只是想知道背后的理由和考虑。

我想知道 C++11 标准所说的“为什么”而不是“什么”。

4

4 回答 4

23

没有真正的理由 lambda 不能有默认参数。但是,使用 lambda 有两种主要方法,其中只有一种允许默认参数而不更改类型系统。

  1. 您可以直接调用 lambda,也可以通过模板调用。在这种情况下,默认参数可以正常工作。

  2. 您可以通过调用 lambda std::function。如果不更改类型系统,默认参数将不起作用。

我的猜测是人们用 C++11 编写的新函数通常会接受std::function参数,因为这样,函数不必模板,也不必为每个获得的 lambda 和仿函数实例化传递给它。

为什么std::function(或函数指针)不能采用默认值?

这种函数的类型并不明显。

  • 如果是std::function<int(bool)>,那么您如何使用默认值调用它?(你不能。)

  • 如果是std::function<int(bool=false)>,那么它与哪些类型兼容,转换如何工作?你能把它转换成std::function<int()>? 怎么样std::function<int(bool=true)>

  • 如果它是新的std::function<int(bool=default)>那么它与哪些类型兼容,转换如何工作?

基本上,这不仅仅是一个您可以在标准中翻转并制作函数指针/std::function处理默认参数的开关。普通函数中的默认参数是使用函数声明中的信息处理的,这些信息在 lambda 或函数指针的调用站点不可用。因此,您必须将有关默认值的信息编码到函数类型中,然后为转换和兼容性制定所有非显而易见的规则。

因此,您必须提出一个令人信服的理由来说明为什么要添加这样的功能,并说服委员会。

那么,为什么 lambda 不能采用默认值呢?

我没有回答这个问题。但我认为添加它不会是一个非常有用的功能。如果可以的话,我会删除这个答案,但它已被接受。如果可以的话,我会投反对票,但这是我的。这就是生活。

于 2013-01-07T04:19:09.407 回答
5

我同意在某些情况下允许 lambdas 中的默认参数工作本身并没有真正的“技术”限制。它不会破坏您的指针 and ,因为函数的类型不受默认参数的影响。但这也是为什么这不是非常实用的原因。auto

为什么?

因为默认参数虽然是函数签名的一部分,但不是函数类型的一部分:

[C++11: 1.3.17]:
签名
<function> 名称、参数类型列表 (8.3.5) 和封闭命名空间(如果有)
[注意:签名用作名称修改和链接的基础。——尾注]

[C++11: 8.3.5/6]: [..]返回类型、parameter-type-listref-qualifiercv-qualifier-seq,但不是默认参数 (8.3.6) 或异常规范 (15.4),是函数类型。[注:函数类型在函数指针、函数引用和成员函数指针的赋值和初始化过程中检查。——尾注]

它们本质上是一块语法糖,由编译器“激活”,能够看到您使用的函数的声明,并在函数调用时注入:

#include <iostream>

void foo(int x = 5)
{
   std::cout << x << '\n';
}

int main()
{
   foo();
}
  • 输出:5

默认参数是“可见的”。

但是,当您将函数“隐藏”在指针后面时:

int main()
{
    void (*bar)(int) = &foo;
    bar();
}
  • 错误:too few arguments to function

of 的类型bar是正确的,编译器知道它foo有一个默认值,但是根本没有直接bar的语法可以在调用that baris时通知编译器foo。当然,在这种微不足道的场景中,它可以通过观察分配来解决,但这很难成为更广泛论点的理由。

出于同样的原因,仅在调用站点不可见的定义中声明的默认参数几乎是无用的:

// a.h
void foo(int);

// a.cpp
#include "a.h"
#include <iostream>

void foo(int x = 5)
{
   std::cout << x << '\n';
}

// main.cpp
#include "a.h"

int main()
{
    foo();
}
  • main.cpp 中的错误:too few arguments to function

我想这是因为:

[C++11: 8.3.6/4]: [..]不同范围的声明具有完全不同的默认参数集。[..]

我们允许为类非模板成员函数定义()“堆积”默认参数[C++11 8.3.6/6];该示例表明此默认值仍将仅适用于同一 TU,这遵循我们在上面的第二个代码片段中看到的行为。

因此,如果默认参数不是函数类型的一部分,并且必须对调用站点明确可见,那么只有少数人为设计的极端情况对 lambdas 有用,即在调用它们时在创建它们的相同范围内,因此编译器可以轻松地弄清楚如何“填充”调用 lambda 的默认参数,那么,这有什么意义呢?不是很多,我告诉你。

于 2013-01-07T12:56:56.637 回答
3

正如对该问题的评论所暗示的那样,可能没有技术上的理由不存在默认参数。但另一个问题是“默认参数有实际原因吗?” 我认为这个问题的答案是“不”,这就是原因。

为了调用 lambda,您可以做的一件事是立即调用它

[] { printf("foo"); }();

我敢肯定这有有限的用途,如果有的话,所以让我们继续。调用 lambda 的唯一另一种方法是首先将其绑定到变量,这里有几个选项。

  1. 使用自动。所以我们得到auto foo = [] { printf("foo"); }; foo();
  2. 将其绑定到函数指针:void (*foo)() = [] { printf("foo"); }; foo(). 当然,这仅在 lambda 未捕获时才有效。
  3. 通过将 lambda 传递给函数或函数模板来执行 1 或 2 的等效操作。

现在让我们来看看默认参数在每种情况下的用处

  1. 在这种情况下,我们直接调用 lambda,因此代码可能足够紧密,我们不会使用各种数量的参数调用 lambda。如果我们是,lambda 可以(应该?)可能被重构为更通用的组件。我在这里看不到任何实际好处。
  2. (见1)
  3. 我们将 lambda 传递给另一个函数。我也没有看到这里默认参数的实际好处。回想一下好的旧仿函数(可以有默认参数)——我不能说我知道有太多函数依赖于默认参数,即使这些函数也存在。由于 lambdas 实际上只是函子,因此没有理由让这种观察突然改变。

我认为这些点足以说明 lambda 的默认参数真的没那么有用。另外,我看到有些人在谈论 lambda 类型的问题,如果它有默认参数,但这是一个非 IMO 问题。您始终可以编写自己的仿函数来执行相同的操作,并且确实具有默认参数。另外,关于降级为函数指针,也没什么好说的。取正常功​​能

void func(int i = 0)
{
}

并把它的地址。你得到了什么?一个void (*)(int)。没有理由 lambda 会遵循不同的规则。

于 2013-01-07T04:12:46.207 回答
0

我已经阅读了其中一些回复,每个人(从我读到的 tl;dr)都指的是类型系统中的限制。这真的和那个没有任何关系。尽管函数的类型包括所有参数,无论是否默认,但具有默认参数的函子可以被认为是一个模糊函数,直到您查看它才产生类型。

由于 lambda 是简化函子使用的语法糖,并带有一些很酷的类型推导。当它被编译器归结并转换时,当它有捕获时是一个函子(如果没有捕获,则为一个函数)。所以真的没有理由不能在 lambda 中使用它。观察:

#include<functional>
#include<iostream>

int main()
{
    // my lambda as written as a functor.  Not pretty eh?  But it works. o.O
    struct {
    void operator()(char const* x = "default") {
         std::cout << x << std::endl;
    } } fn;

    fn("Hi");
    fn();
    std::function<void(char const*)> fn_copy_1 = fn;
    std::function<void()>            fn_copy_0 = fn;
    fn_copy_1("there");
    fn_copy_0();
    return 0;
}

结果(此处运行示例)

Hi
default
there
default

所有这些都无需对类型系统进行任何更改。

那么,为什么会有这样的限制呢?我的猜测,没有人认为它会有用,如果不实施,测试的东西就会更少。用户称他们需要功能 X 时,会将事物添加到语言中。如果没有人要求它并且没有足够的价值与之关联,那么它就不会被添加。这并不是说以后不能添加,而是只有在有需求的情况下才可以添加。

于 2021-10-30T19:49:37.637 回答