【问题标题】:vector push_back shows absurd result矢量 push_back 显示荒谬的结果
【发布时间】:2015-02-08 23:48:29
【问题描述】:

我试图通过重载运算符

打印输出应为 1 2 3 4。

但它实际上会打印出如下内容:28495936 0 3 4。

应该被推入向量的前两个元素(例如 1 和 2)丢失或被污染。

如果有人能帮我弄清楚这背后的原因,我将不胜感激。

#include <iostream>
#include <vector>

using namespace std;

template<typename T>
class make_vector {
public:
    typedef make_vector<T> my_type;
    my_type& operator<<(const T& val)
    {
        data_.push_back(val);
        return *this;
    }
    operator std::vector<T>&()
    {
        return  this->data_;
    }

private:
    std::vector<T> data_;
};


int main() {
    std::vector<int>&  A2 = make_vector<int>() << 1 << 2 << 3 << 4;

    for (std::vector<int>::iterator it = A2.begin(); it != A2.end(); ++it)        
    {
        cout << *it << " ";
    }
    cout << endl;
    return 0;
}

【问题讨论】:

    标签: c++ vector type-conversion operator-overloading push-back


    【解决方案1】:

    您正在将左值引用绑定到临时对象:

    std::vector<int>&  A2 = make_vector<int>() << 1 << 2 << 3 << 4;
    

    请注意,@T.C.在 cmets 中提到,是您的转换运算符启用此功能。如果没有它,上面的语句将是无效的 C++。

    您的代码的问题是,在这一行之后,A2 指的是一个已失效的对象。

    在我看来你不想使用参考:

    std::vector<int>  A2 = make_vector<int>() << 1 << 2 << 3 << 4;
    

    【讨论】:

    • 我现在看到了问题。它有助于!感谢您的澄清。
    • 由于设计不佳的operator std::vector&lt;T&gt;&amp;(),您不需要扩展来编译它。它真的应该是重新合格的;和 operator&lt;&lt; 可能还应该有两个 ref-qualified 重载,以便您保留值类别。
    • @T.C.我完全错过了。谢谢!
    【解决方案2】:

    你在这里有一个悬空的参考。它引用了一个临时的。

    std::vector<int>&  A2 = make_vector<int>()....
    

    你有两个选择:

    您可以复制临时变量到一个新的局部变量中。

    std::vector<int>  A2 = make_vector<int>()....
    

    使用const ref。 C++ 授予一个特殊规则以允许 const 引用延长临时对象的生命周期。

    const std::vector<int>&  A2 = make_vector<int>()....
    

    【讨论】:

    • 知道了。谢谢你,德鲁。
    • const ref 不起作用。您正在绑定到成员函数的返回值,而不是直接绑定到临时函数;这不会延长使用寿命。
    • @T.C.我从来不知道。感谢您的提示!
    【解决方案3】:

    您的make_vector 设计不当,使用起来很危险。即使 make_vector 对象是临时对象,您也可以将其转换为 std::vector&lt;T&gt;&amp; 引用其 vector 成员。这是一个等待发生的悬空引用,因为使用此设置编译器不会在您执行诸如

    之类的操作时抱怨
    std::vector<int>&  A2 = make_vector<int>() << 1 << 2 << 3 << 4;
    

    因为这一行中的所有内容都是完全有效的。除了它留下A2 一个悬空引用作为make_vector 临时的——以及它包含的向量——在; 处被销毁。哎哟。

    更好的设计会重新限定和重载您的 operator&lt;&lt; 和转换函数:

    my_type& operator<<(const T& val) &
    {
        data_.push_back(val);
        return *this;
    }
    my_type&& operator<<(const T& val) &&
    {
        data_.push_back(val);
        return std::move(*this);
    }
    
    operator std::vector<T>&() &
    {
        return  this->data_;
    }
    
    operator std::vector<T>() &&
    {
        return  std::move(this->data_);
    }
    

    首先,我们只将左值make_vectors 转换为对底层向量的左值引用,因为这可能是安全的。对于右值make_vectors,比如temporaries,我们让转换函数通过从底层向量移动,按值返回向量。

    其次,我们重载operator&lt;&lt; 以保留调用它的make_vector 对象的值类别——如果在左值上调用它会返回左值引用,如果在右值上调用则返回右值引用。这样,make_vector&lt;int&gt;() &lt;&lt; 1 &lt;&lt; 2 &lt;&lt; 3 &lt;&lt; 4 仍然是一个右值。

    现在像std::vector&lt;int&gt;&amp; A2 = make_vector&lt;int&gt;() &lt;&lt; 1 &lt;&lt; 2 &lt;&lt; 3 &lt;&lt; 4; 这样的错误不会编译,而两者都

    std::vector<int> A2 = make_vector<int>() << 1 << 2 << 3 << 4;
    

    const std::vector<int> & A2 = make_vector<int>() << 1 << 2 << 3 << 4;
    

    will 和两者都是安全的。 (在第二种情况下,const 引用绑定到 operator std::vector&lt;int&gt;() 返回的临时值,从而延长了临时值的生命周期。)

    【讨论】:

    • T.C.,非常感谢您的回答。现在有足够的信息让我消化和学习。我会确保在以后获得足够的声誉后,我可以投票赞成这个答案。
    • 是否应该将第一个转换函数改为运算符 std::vector() & ?
    • @user1487243 不需要;关键是当对象是左值时,返回引用是安全的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-22
    • 1970-01-01
    • 1970-01-01
    • 2020-08-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多