【问题标题】:C++11 rvalue object fieldC++11 右值对象字段
【发布时间】:2014-05-17 11:42:26
【问题描述】:

我可以在 c++11 中使用带有右值字段的类/结构吗? 喜欢这个:

template<typename T>
struct RvalueTest{
    RvalueTest(T&& value) : value( std::forward<T>(value) ){}
    T&& value;
};

因为在下面的测试中:

class Widget {
public:
  Widget(){std::cout << "Widget ctor  " << std::endl; }
  Widget(int h) : h(h){std::cout << "Widget ctor param " << std::endl; }

  Widget(const Widget&) { std::cout << "Widget copy ctor  " << std::endl;  }
  Widget(Widget&&) { std::cout << "Widget move ctor  " << std::endl;  }           // added this

  template<typename T>
  Widget(const T&) { std::cout << "Generalized Widget copy ctor  " << std::endl;  }

  template<typename T>
  Widget(T&&) { std::cout << "Universal Widget ctor  " << std::endl;  }

  int h;
};

RvalueTest<Widget> r(Widget(12));
std::cout << r.value.h;

我在输出时得到了一些垃圾值(使用 -O2):
http://coliru.stacked-crooked.com/a/7d7bada1dacf5352

Widget ctor param
4203470

使用 -O0 正确的值:
http://coliru.stacked-crooked.com/a/f29a8469ec179046

Widget ctor param
12

为什么???

附:我试图实现的是单个 ctor 调用,没有任何额外的移动/复制构造函数。


更新

clang 编译正常 http://coliru.stacked-crooked.com/a/6a92105f5f85b943
海湾合作委员会错误?还是按原样工作?

【问题讨论】:

    标签: c++11 rvalue-reference rvalue


    【解决方案1】:

    此问题并非特定于右值引用。如果您将 T&& 替换为 const T&,您将看到相同的奇怪行为。

    template<typename T>
    struct ReferenceTest{
        ReferenceTest(const T& value) : value(value){}
        const T& value;
    };
    

    问题是,您正在存储对临时对象的引用。临时对象在创建它的语句结束时自动销毁,但您稍后尝试通过引用访问它。

    RvalueTest<Widget> r(Widget(12)); // pass reference to temporary object to constructor
    std::cout << r.value.h; // invalid reference
    

    可以使用右值引用作为成员变量。但是,如果这样做,您必须小心。标准库中的一个示例是std::forward_as_tuple

    auto t = std::forward_as_tuple(42);
    // t has type std::tuple<int&&>
    

    关于如何避免复制或移动构造的问题:不要使用右值引用成员变量。这里有两种方法,您可以改用:

    std::unique_ptr:如果复制或移动 Widget 开销太大,您可以使用智能指针:

    template <typename T>
    struct Test
    {
        std::unique_ptr<T> _value;
        explicit Test(std::unique_ptr<T> value) : _value{std::move(value)} { }
    };
    
    Test<Widget> t{std::make_unique<Widget>(12)};
    std::cout << t._value->h;
    

    第二种方法就地创建 Widget。这就是标准容器的不同 emplace 功能所做的,例如std::vector::emplace_back.

    template <typename T>
    struct Test
    {
        T _value;
        template <typename ...Args>
        explicit Test(Args&&... args) : _value{std::forward<Args>(args)...} { }
    };
    
    Test<Widget> t{12};
    std::cout << t._value.h;
    

    【讨论】:

    • 换句话说,我必须如何传递那个“模板”参数 Widget(12) 所以它不会调用额外的构造函数。没有开销。
    • 并且编译器不允许您通过“const T& value”(来自您的示例)传递模板值。它只是在那里显示错误。
    • forward_as_tuple 元组以与我的示例 coliru.stacked-crooked.com/a/302bb62ba3acd5db 相同的方式释放我的数据
    • @tower120:我试图解释为什么你的代码被破坏了,以及右值引用成员变量有有效的用例。 std::forward_as_tuple 的示例并不是为了解决您的问题,而只是作为有效用例的示例。
    • 应该怎么样?我的意思是,如何避免移动/复制构造函数。毕竟 Widget(12) 是临时的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-12
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多