Construction in return statement
假设我们有一个类 Foo,它带有一个来自 int 的非 explicit 构造函数。然后为以下函数:
1 2
| Foo makeFoo1() { return 123; }
Foo makeFoo2() { return {123}; } |
我认为 makeFoo1 要求 Foo 的复制/移动 ctor 是可访问的,并且编译器可能(尽管不太可能)不会删除副本,从而导致真正的复制/移动。
对于 makeFoo2,由于我们使用的是复制列表初始化,因此永远不会发生复制/移动。
我真的应该担心这一点,并尽可能将非explicit ctors 的参数放在大括号中(如在makeFoo2 中)? (假设我是库作者,并希望该库与嵌入式系统的低于标准的编译器一起使用。)
- "对于 makeFoo2,由于我们使用的是复制列表初始化,因此永远不会发生复制/移动。"真的吗?
-
不,但是第二个 makeFoo 对 Foo 类的要求较低(没有任何副本);因此它更好。
-
你想省略什么副本?如果您从整数构造,我不会担心省略复制整数,因为这将适合 CPU 寄存器。至于 Foo 对象,那不是已经被 RVO 忽略了吗?
-
@Spacemoose 我说的是 RVO。它几乎总是被执行,但标准并不能保证这一点。如果我想确保在任何情况下都不会复制,我猜复制列表初始化应该是最好的选择?
-
根据《Effective Modern C》第 25 条中的 Scott Meyers,该标准表明即使编译器选择不执行复制省略,返回的对象也必须被视为右值(即按照 'return std::move (X);' )
-
请注意,在这种情况下,C 17 标准将要求复制省略("保证复制省略")。
-
我会更关心在你的代码库中有这样的隐式构造函数!
我会在这里冒险,将一个实用的答案与一个薄弱的语言法律理由结合起来。
弱语言律师论点:如果我正确理解了这个 cppreference.com 描述,那么在使用初始化列表 (makeFoo2) 初始化时,与仅使用 int 返回 (makeFoo1) 相比,不能保证 RVO )。所以,不要用大括号把你的 int 括起来。
实用论点1:来吧,那些大括号必须是多余的。它们应该是多余的。所以不要使用它们,它似乎不对。
实用论证2:最小意外原则。使用这些大括号,您是在暗示无大括号的形式有问题。我会对大括号感到困惑和略微惊讶(尽管不是很困惑)。
实用论点 3:在这些情况下 - 相信编译器。这不是您的性能瓶颈所在(如果是 - 将其设为内联函数,但不会是;或者,如果您有现有对象,请使用引用;等等)。