Astd::optional
不是函数参数默认值的直接替换:
void compute_something(int a, int b, const Object& c = Object(whatever))
这可以被调用compute_something(0, 0);
void compute_something(int a, int b, std::optional<Object> c)
这无法编译。compute_something(0, 0);
不会编译。至少,你必须做一个compute_something(0, 0, std::nullopt);
.
因此,每次我们使用 std::optional 来传递参数时,它都意味着创建副本,如果对象很大,则可能是一种惩罚性能。
正确的。但请注意,还需要构造一个默认的函数参数。
std::optional
但是您可以通过结合std::reference_wrapper来做一些技巧:
#include <optional>
#include <utility>
#include <functional>
#include <iostream>
class X {
public:
X()
{
std::cout << "Constructor" << std::endl;
}
~X()
{
std::cout << "Destructor" << std::endl;
}
void foo() const
{
std::cout << "Foo" << std::endl;
}
X(const X &x)
{
std::cout << "Copy constructor" << std::endl;
}
X &operator=(const X &)
{
std::cout << "operator=" << std::endl;
}
};
void bar(std::optional<std::reference_wrapper<const X>> arg)
{
if (arg)
arg->get().foo();
}
int main()
{
X x;
bar(std::nullopt);
bar(x);
return 0;
}
使用 gcc 7.2.1,唯一的输出是:
Constructor
Foo
Destructor
这确实增加了一些语法,并且可能很麻烦。但是,一些额外的语法糖可以减轻额外的绒毛。例如:
if (arg)
{
const X &x=arg->get();
// Going forward, just use x, such as:
x.foo();
}
现在,让我们再迈出一步:
void bar(std::optional<std::reference_wrapper<const X>> arg=std::nullopt)
这样,两个函数调用就可以简单地是:
bar();
bar(x);
你可以吃蛋糕,也可以吃。您不必显式提供 a std::nullopt
,由默认参数值提供;你不必构造一个完整的默认对象,当显式传递一个对象时,它仍然通过引用传递。你只是有自己的开销std::optional
,在大多数 C++ 实现中,这只是几个额外的字节。