0

我正在尝试获取rvalue此类的一个实例:

#include <iostream>
#define msg(x) std::cout << x " constructor\n"

struct X {
    int i;
    X(int i) : i(i) {msg("X");}
    X(const X& x) : i(x.i) {std::cout << "X copy\n";}
    X(X&& x) {std::swap(i, x.i); std::cout << "X move\n";}
};

x进入这个类的实例变量:

struct A {
    X x;
    A(X x) : x(x) {msg("A");}
};

像这样:

int main() {
    A a(X(1));
    std::cout << a.x.i << "\n\n";
}

没有任何复制或移动

根据这些参考资料,

还有很多关于 SO 的帖子(所以请在标记为重复之前阅读到最后),我应该依赖复制省略,如果我通过值传递应该满足其条件。请注意,需要两个复制省略,即:

constructor call -> constructor local variable -> instance variable

关闭复制省略时可以看到(使用 编译g++-4.8 -std=c++11 -fno-elide-constructors):

X constructor
X move
X copy
A constructor
1

所以有一个move步骤和一个copy步骤,如果我打开复制省略(用 编译),这两个步骤都应该消失g++-4.8 -std=c++11 -O3

X constructor
X copy
A constructor
1

糟了,还剩copy一步!


我可以通过任何其他变体std::move()std::forward传递为更好rvalue-reference吗?

struct B {
    X x;
    B(X x) : x(std::move(x)) {msg("B");}
};

struct C {
    X x;
    C(X x) : x(std::forward<X>(x)) {msg("C");}
};

struct D {
    X x;
    D(X&& x) : x(std::move(x)) {msg("D");}
};

int main() {
    B b(X(2));
    std::cout << b.x.i << "\n\n";

    C c(X(3));
    std::cout << c.x.i << "\n\n";

    D d(X(4));
    std::cout << d.x.i << "\n\n";
}

产生输出:

X constructor
X move
B constructor
2

X constructor
X move
C constructor
3

X constructor
X move
D constructor
4

好的,我把copy变成了move但这并不令人满意!


接下来,我尝试使实例变量x成为引用X&

struct E {
    X& x;
    E(X x) : x(x) {msg("E");}
};

int main() {
    E e(X(5));
    std::cout << e.x.i << "\n\n";
}

产生:

X constructor
E constructor
1690870696

馊主意!我摆脱了move但引用的rvalue实例x在我的座位下被破坏了,所以最后一行打印垃圾而不是5. 两个注意事项:

  • g++-4.8没有警告我任何事情,即使有-pedantic -Wall -Wextra
  • 5程序在编译时打印-O0

所以这个错误可能会在很长一段时间内被忽视!


那么,这是一个没有希望的案例吗?没有:

struct F {
    X& x;
    F(X& x) : x(x) {msg("F");}
};

int main() {
    X x(6);
    F f(x);
    std::cout << f.x.i << "\n";
}

印刷:

X constructor
F constructor
6

真的吗?没有花哨的新C++11功能,没有编译器自行决定的复制省略,只是普通的旧 FORTRAN66 样式的传递引用可以满足我的要求,并且可能会表现最好?

所以这是我的问题:

  • 有什么办法可以让它工作rvalues吗?我错过了任何功能吗?
  • -referencelvalue版本真的是最好的,还是有隐藏的成本?X x(6)
  • x建造后生活会不会带来任何不便f
  • 我可以为使用对lvalue外部实例的引用支付数据局部性罚款吗?
4

1 回答 1

2

无需过多详细说明您的问题,基本上尽可能多地使用复制省略。这是一个快速演示:

#include <iostream>
#include <utility>

struct X
{
    int n_;

    explicit X(int n) : n_(n) { std::cout << "Construct: " << n_ << "\n"; }
    X(X const & rhs) : n_(rhs.n_) { std::cout << "X copy:" << n_ << "\n"; }
    X(X && rhs) : n_(rhs.n_) { rhs.n_ = -1; std::cout << "X move:" << n_ << "\n"; }
   ~X() { std::cout << "Destroy: " << n_ << "\n"; }
};

struct A
{
    explicit A(X x) : x_(std::move(x)) {};
    X x_;
};

struct B
{
    X x;
};

int main()
{
    A a(X(12));
    B b { X(24) };
}

产生

Construct: 12
X move:12
Destroy: -1
Construct: 24
Destroy: 24
Destroy: 12

一个移动x_(std::move(x))是不可省略的,因为它不涉及函数返回。但这无论如何都很好。并注意聚合b确实是如何“就地”初始化的。


您的示例F表明您愿意将 的耦合公开X到其环境类。在这种情况下,您可以A为该构造X直接创建一个特殊的构造函数:

struct A
{
    explicit A(X x) : x_(std::move(x)) {};
    X x_;

    // Better approach
    struct direct_x_t {};
    static direct_x_t direct_x;

    // In our case, this suffices:
    A(direct_x_t, int n) : x_(n) {}

    // Generally, a template may be better: (TODO: add SFINAE control)
    template <typename ...Args> A(direct_x_t, Args &&... args)
    : x_(std::forward<Args>(args)...) {}
};

用法:

A a(A::direct_x, 36);
于 2014-05-29T22:24:08.463 回答