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

塔120

为什么在具有带有通用引用参数的构造函数的类中不进行右值优化?

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
TC

我认为问题在于

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

就语言而言,它们不是复制/移动构造函数,因此编译器无法消除对其的调用。从§12.8[class.copy] / p2-3起,添加了重点,并省略了示例:

一个非模板的构造函数的类X是拷贝构造函数,如果它的第一个参数是类型X&const X&volatile X&const volatile X&,并且或者有没有其他参数,否则所有其他参数都默认参数(8.3.6)。

一个非模板的构造函数的类X是移动构造函数,如果它的第一个参数是类型的X&&const X&&volatile X&&,或const volatile X&&,并且或者有没有其他参数,否则所有其他参数默认参数(8.3.6)。

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

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

当满足某些条件时,即使为复制/移动操作选择的构造函数和/或对象的析构函数具有副作用,也允许实现忽略类对象的复制/移动构造。

这使得实现可以消除“复制/移动构造”;使用既不是复制构造函数也不是move构造函数的对象构造对象不是“复制/移动构造”。

由于C具有用户定义的析构函数,因此不会生成隐式move构造函数。因此,重载解析将选择Args推导为的模板化构造函数C,这比右值的隐式副本构造函数更好。但是,编译器无法取消对此构造函数的调用,因为它具有副作用,并且既不是复制构造函数也不是move构造函数。

如果改为使用模板构造函数

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

然后它不能用Args=实例化C以生成副本构造函数,因为那样会导致无限递归。标准中有一条特殊规则禁止此类构造函数和实例化(第12.8节[class.copy] / p6):

如果类的构造函数声明的X第一个参数是类型(可选,用cv限定),X并且没有其他参数,或者所有其他参数都具有默认参数,则该声明的格式不正确。成员函数模板从不实例化以生成此类构造函数签名。

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

如果我们改为从中删除自定义析构函数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的move构造函数是由过载解析隐式生成和选择的,您会再次看到RVO插入。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类常见问题

C ++重载通用方法,按引用和按值

来自分类Dev

在C ++中通过引用返回值

来自分类Dev

为什么函数指针没有被通用引用捕获?

来自分类Dev

带有引用的无效函数与在C ++中返回值的函数

来自分类Dev

类中没有通用引用的通用类型方法

来自分类Dev

C ++中的函数如何以最少的复制量返回值或引用?

来自分类Dev

定义和引用Play模板签名中绑定的通用类型

来自分类Dev

引用对象中的通用值

来自分类Dev

C ++显式通用引用构造函数不会隐藏副本构造函数吗?

来自分类Dev

C#通用自引用声明

来自分类Dev

在Java中引用“嵌套”通用参数

来自分类Dev

从记录中引用通用值

来自分类Dev

通用引用和重载

来自分类Dev

C ++引用和返回值

来自分类Dev

通用List <T> .Contains()中的值与引用相等

来自分类Dev

构造函数中的C ++封装和引用

来自分类Dev

如何在C ++中使用通用引用参数为模板类编写构造函数

来自分类Dev

C ++重载通用方法,按引用和按值

来自分类Dev

在C ++中通过引用返回值

来自分类Dev

C ++ std :: bind通用引用无法编译

来自分类Dev

从通用数组返回引用

来自分类Dev

C ++中的函数如何以最少的复制量返回值或引用?

来自分类Dev

通用引用和本地类

来自分类Dev

在C ++中创建具有按引用调用返回值的类函数

来自分类Dev

C ++显式通用引用构造函数不会隐藏副本构造函数吗?

来自分类Dev

成员函数指针中的通用引用

来自分类Dev

在javascript中解析通用id引用的json

来自分类Dev

无法从 Objective-C 通用框架引用 Swift 通用框架的类和方法

来自分类Dev

如何将引用值分配给通用属性