You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于C++11右值引用在Stroustrup示例构造函数中的使用疑问

Understanding Value Categories and Move Semantics in Your C++ Example

Great question—let’s unpack this to see why Stroustrup’s original code uses a value parameter, and whether switching to a rvalue reference is a good idea.

First, let’s recap the code snippets for clarity:

Original Code (from the book)

struct Point{ int x,y; };
struct Points{ 
    vector<Point> elem; 
    Points(Point p0){elem.push_back(p0)}; 
    // ... other members
};
Point x2{ {100,200} };

Your Proposed Change

struct Points{ 
    vector<Point> elem; 
    Points(Point&& p0){elem.push_back(p0)}; 
    // ... other members
};

Why the Original Version Works (and Has Benefits)

The original constructor uses pass-by-value, which has two key advantages:

  • Compatibility: It works with both lvalues (like x2, which has a persistent address you can take with &) and rvalues (like temporary objects such as Point{1,2}). If you only provide the rvalue reference version, you can’t initialize a Points instance with an lvalue like x2—that would trigger a compilation error, since lvalues can’t bind to non-const rvalue references.
  • Simplicity: For small, trivial types like Point (which just holds two ints), the cost of copying is negligible. The difference between copying and moving such an object is effectively zero, so optimizing here doesn’t gain you anything. Stroustrup might have chosen this version to keep the example focused on basic constructor mechanics before diving into more complex move semantics.

Issues with Your Rvalue Reference Version

While using a rvalue reference could enable moving p0 into the vector (note: your code still does a copy! Because p0 is a named rvalue reference, which is treated as an lvalue inside the function—you’d need elem.push_back(std::move(p0)); to actually move it), the bigger problem is limited usability. As mentioned, you can’t pass lvalues to this constructor. That’s a major downside unless your only use case for Points is with temporary Point objects, which is unlikely.

A Better Middle Ground (If You Want to Optimize)

If you want to support both lvalues and rvalues while optimizing moves for rvalues, you have a couple of solid options:

  1. Provide Two Overloads:

    struct Points{ 
        vector<Point> elem; 
        // Accept lvalues, copy into the vector (safe for values you need to keep using)
        Points(const Point& p0){elem.push_back(p0)};
        // Accept rvalues, move into the vector (avoids copies for temporaries)
        Points(Point&& p0){elem.push_back(std::move(p0))};
        // ... other members
    };
    

    This approach keeps things explicit: lvalues get copied, rvalues get moved, and all valid use cases are supported.

  2. Use Perfect Forwarding:
    If you want to handle any type that can be converted to Point (not just Point itself), you can use a template with std::forward:

    struct Points{ 
        vector<Point> elem; 
        template<typename T>
        Points(T&& p0){elem.push_back(std::forward<T>(p0))};
        // ... other members
    };
    

    This forwards the original value category of p0 to push_back, so lvalues are copied and rvalues are moved. Just note that this adds a bit of complexity (like handling unexpected implicit conversions), so it’s best used when you need that flexibility.

Final Takeaway

Your proposed rvalue reference constructor isn’t ideal because it sacrifices compatibility with lvalues for minimal (if any) performance gain with trivial types like Point. The original version is clearer and more flexible, especially for a teaching example. If you do want to optimize move operations, go with the overloaded approach or perfect forwarding instead—they balance performance and usability perfectly.

内容的提问来源于stack exchange,提问作者nodeover

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:34:22