gpt4 book ai didi

C++构造函数中的通用引用和返回值优化(rvo)

转载 作者:可可西里 更新时间:2023-11-01 16:36:53 27 4
gpt4 key购买 nike

为什么右值优化不会发生在具有通用引用参数的构造函数的类中?

http://coliru.stacked-crooked.com/a/672f10c129fe29a0

#include <iostream>

template<class ...ArgsIn>
struct C {

template<class ...Args>
C(Args&& ... args) {std::cout << "Ctr\n";} // rvo occurs without &&

~C(){std::cout << "Dstr\n";}
};

template<class ...Args>
auto f(Args ... args) {
int i = 1;
return C<>(i, i, i);
}

int main() {
auto obj = f();
}

输出:

Ctr
Ctr
Dstr
Ctr
Dstr
Dstr

最佳答案

我认为问题在于实例化

template<class ...Args>
C(Args&& ... args) {std::cout << "Ctr\n";}

就语言而言不是复制/移动构造函数,因此编译器不能省略对它们的调用。来自 §12.8 [class.copy]/p2-3,强调并省略示例:

A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments (8.3.6).

A non-template constructor for class X is a move constructor if its first parameter is of type X&&, const X&&, volatile X&&, or const volatile X&&, and either there are no other parameters or else all other parameters have default arguments (8.3.6).

换句话说,作为模板的构造函数永远不能是复制或移动构造函数。

返回值优化是复制省略的一个特例,描述为(§12.8 [class.copy]/p31):

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects.

这允许实现省略“复制/移动构造”;使用既不是复制构造函数也不是移动构造函数的东西构造对象不是“复制/移动构造”。

因为 C 有一个用户定义的析构函数,所以不会生成隐式移动构造函数。因此,重载决议将选择模板化构造函数,其中 Args 推导为 C,这比右值的隐式复制构造函数更匹配。但是,编译器无法忽略对此构造函数的调用,因为它具有副作用并且既不是复制构造函数也不是移动构造函数。

如果模板构造函数是

template<class ...Args>
C(Args ... args) {std::cout << "Ctr\n";}

那么它就不能用 Args = C 实例化来产生一个复制构造函数,因为那样会导致无限递归。标准中有一条特殊规则禁止此类构造函数和实例化 (§12.8 [class.copy]/p6):

A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-qualified) X and either there are no other parameters or else all other parameters have default arguments. A member function template is never instantiated to produce such a constructor signature.

因此,在那种情况下,唯一可行的构造函数将是隐式定义的复制构造函数,并且可以省略对该构造函数的调用。

如果我们改为remove C 的自定义析构函数,并添加另一个类来跟踪何时调用 C 的析构函数:

struct D {
~D() { std::cout << "D's Dstr\n"; }
};

template<class ...ArgsIn>
struct C {
template<class ...Args>
C(Args&& ... args) {std::cout << "Ctr\n";}
D d;
};

我们只看到对D 的析构函数的一次调用,表明只构造了一个C 对象。这里 C 的移动构造函数是通过重载决策隐式生成和选择的,您会看到 RVO 再次启动。

关于C++构造函数中的通用引用和返回值优化(rvo),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24925137/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com