在将变量用作emplace_back的参数后使用变量是否可以?

时间:2017-04-03 20:41:20

标签: c++ c++11 language-lawyer

可能是一个蹩脚的问题,但我一直未能找到全面的答案。

std::vector::emplace_back的参数是r值引用。据我所知,在通过r值引用传递某个对象后使用它是不安全的。我的意思是:

std::string str("hello world");
std::string str2(std::move(str)); // string::string(string &&);
cout << str;                      // unsafe, str was moved to str2

那么,下面的例子会发生什么?

std::vector<std::string> array;
std::string str("hello world");   // what if add 'const' qualifier here?
array.emplace_back(str);          // template <class... Args>
                                  // void emplace_back (Args&&... args);
std::cout << str;                 // safe or not? str was moved or copied?

我真的很困惑。我的测试显示stremplace_back之后可以安全使用,但是我的(破坏?)逻辑告诉我str被移动了,之后不应该使用。< / p>

PS。抱歉我的英语不好:)

3 个答案:

答案 0 :(得分:9)

emplace - 样式函数的参数是转发引用,这意味着它们成为左值参数的左值引用和rvalue参数的右值引用。

使用

array.emplace_back(str);

str是一个左值(你没有将它转换为std::move的右值),因此它将被复制。它将在通话后保留其价值。

答案 1 :(得分:1)

emplace_back将复制l值,并移动r值。

有人可能会用一个简单的例子对此进行测试:

struct test {
    test() {
        std::cout << "Default-constructed\n";
    }

    test(test const&) {
        std::cout << "Copy-constructed\n";
    }

    test(test &&) {
        std::cout << "Move-constructed\n";
    }

    ~test() {
        std::cout << "Destructed\n";
    }

    test& operator=(test const&) {
        std::cout << "Copy-assigned\n";
        return *this;
    }

    test& operator=(test &&) {
        std::cout << "Move-assigned\n";
        return *this;
    }
};

int main() {
    std::vector<test> vector;
    test t;
    vector.emplace_back(t);//The important one
    vector.emplace_back(test{});
    return 0;
}

这(假设copy-ellision不适用于此处)导致以下输出:

Default-constructed
Copy-constructed //The important one
Move-constructed
Destructed
Destructed
Destructed

请注意,当使用l值调用emplace_back时,会调用复制构造函数。因此,在您的情况下,字符串将被复制,而不是移动,因此可以安全地继续在向量之外使用。

值得注意的是,Move-Semantics通常要求移动的对象“处于未指定但有效的状态”,这意味着使用移动对象实际上不应该是“不安全”。它仍然可以产生奇怪的效果,并且可以调用未定义的行为,具体取决于该对象的有效状态可以包含的内容(例如,如果您尝试取消引用移动的unique_ptr或其他类似对象)。

答案 2 :(得分:1)

标准库对象通常处于“有效但未指定的状态”。

  

有效但未指明的状态
  除了满足对象的不变量和对其的操作之外,未指定的对象的值   对象的行为与其类型相同   [示例:如果LOCALE_IDEFAULTCODEPAGE类型的对象x处于有效但未指定的状态,std::vector<int>可以是   无条件调用,只有x.empty()返回x.front()时才能调用x.empty() -end example ]

这通常意味着空或保留原始值。移动false可能不会重置其值。

某些类型更具指定性,例如int在移动后始终保持nullptr。

所以,在这种情况下

unique_ptr

代码有效,但我们不确切知道输出是什么,如果有的话。使它不那么有用。

这个想法是你应该在移动之后让变量超出范围,或者为其分配一个新值以进一步使用它。