我认为以下代码非常方便且无害:
auto fn = [](bool b = false) -> int // NOT legal in C++11
{
return b ? 1 : 0;
};
为什么 C++11 明确禁止 lambda 表达式的默认参数?
我只是想知道背后的理由和考虑。
我想知道 C++11 标准所说的“为什么”而不是“什么”。
我认为以下代码非常方便且无害:
auto fn = [](bool b = false) -> int // NOT legal in C++11
{
return b ? 1 : 0;
};
为什么 C++11 明确禁止 lambda 表达式的默认参数?
我只是想知道背后的理由和考虑。
我想知道 C++11 标准所说的“为什么”而不是“什么”。
没有真正的理由 lambda 不能有默认参数。但是,使用 lambda 有两种主要方法,其中只有一种允许默认参数而不更改类型系统。
您可以直接调用 lambda,也可以通过模板调用。在这种情况下,默认参数可以正常工作。
您可以通过调用 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 或函数指针的调用站点不可用。因此,您必须将有关默认值的信息编码到函数类型中,然后为转换和兼容性制定所有非显而易见的规则。
因此,您必须提出一个令人信服的理由来说明为什么要添加这样的功能,并说服委员会。
我没有回答这个问题。但我认为添加它不会是一个非常有用的功能。如果可以的话,我会删除这个答案,但它已被接受。如果可以的话,我会投反对票,但这是我的。这就是生活。
我同意在某些情况下允许 lambdas 中的默认参数工作本身并没有真正的“技术”限制。它不会破坏您的指针 and ,因为函数的类型不受默认参数的影响。但这也是为什么这不是非常实用的原因。auto
为什么?
因为默认参数虽然是函数签名的一部分,但不是函数类型的一部分:
[C++11: 1.3.17]:
签名
<function> 名称、参数类型列表 (8.3.5) 和封闭命名空间(如果有)
[注意:签名用作名称修改和链接的基础。——尾注]
[C++11: 8.3.5/6]:
[..]返回类型、parameter-type-list、ref-qualifier和cv-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 bar
is时通知编译器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();
}
too few arguments to function
我想这是因为:
[C++11: 8.3.6/4]:
[..]不同范围的声明具有完全不同的默认参数集。[..]
我们被允许为类非模板成员函数定义()“堆积”默认参数[C++11 8.3.6/6]
;该示例表明此默认值仍将仅适用于同一 TU,这遵循我们在上面的第二个代码片段中看到的行为。
因此,如果默认参数不是函数类型的一部分,并且必须对调用站点明确可见,那么只有少数人为设计的极端情况对 lambdas 有用,即在调用它们时在创建它们的相同范围内,因此编译器可以轻松地弄清楚如何“填充”调用 lambda 的默认参数,那么,这有什么意义呢?不是很多,我告诉你。
正如对该问题的评论所暗示的那样,可能没有技术上的理由不存在默认参数。但另一个问题是“默认参数有实际原因吗?” 我认为这个问题的答案是“不”,这就是原因。
为了调用 lambda,您可以做的一件事是立即调用它
[] { printf("foo"); }();
我敢肯定这有有限的用途,如果有的话,所以让我们继续。调用 lambda 的唯一另一种方法是首先将其绑定到变量,这里有几个选项。
auto foo = [] { printf("foo"); }; foo();
void (*foo)() = [] { printf("foo"); }; foo()
. 当然,这仅在 lambda 未捕获时才有效。现在让我们来看看默认参数在每种情况下的用处
我认为这些点足以说明 lambda 的默认参数真的没那么有用。另外,我看到有些人在谈论 lambda 类型的问题,如果它有默认参数,但这是一个非 IMO 问题。您始终可以编写自己的仿函数来执行相同的操作,并且确实具有默认参数。另外,关于降级为函数指针,也没什么好说的。取正常功能
void func(int i = 0)
{
}
并把它的地址。你得到了什么?一个void (*)(int)
。没有理由 lambda 会遵循不同的规则。
我已经阅读了其中一些回复,每个人(从我读到的 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 时,会将事物添加到语言中。如果没有人要求它并且没有足够的价值与之关联,那么它就不会被添加。这并不是说以后不能添加,而是只有在有需求的情况下才可以添加。