简体   繁体   中英

C++11: std::move() call on arguments' list

Is it safe to operate on object within arguments' list, when there is also std::move() invoked on that object ?

void foo(int* raw, std::unique_ptr<int> u)
{
    *raw = 456;
}

std::unique_ptr<int> p(new int(123));
foo(p.get(), std::move(p));

Will the `raw' pointer in foo() be valid if std::move(p) was evaluated as the first parameter ?

No, it's NOT safe. the eval order of argument is not specified in standard. So your code can be run as:

  1. std::move(p) .
  2. call move constructor of std::unique_ptr<int> .
  3. p.get() (because of 2., this will be nullptr .) and pass this parameter.
  4. call foo .

You have to do like this:

int *raw = p.get();
foo(raw, std::move(p));

Notice that your code can work well, because some compilers can compile your code into 3 -> 1 -> 2 -> 4. However, it doesn't mean code is safe. it's not specified standard >o<

Here are answers about argument evaluation order - In short: the order is not specified in standard and may be different per platform, compiler and calling convention.

But I wanted to test it so here are results for Cygwin GCC:

#include <iostream>
#include <memory>
using namespace std;
void print(int* p) {
    cout << (p == nullptr ? "null" : "valid") << endl; }
void foo(int* p, unique_ptr<int> u) {
    print(p); }
void bar(unique_ptr<int> u, int* p) {
    print(p); }
__stdcall void foo2(int* p, unique_ptr<int> u) {
    print(p); }
__stdcall void bar2(unique_ptr<int> u, int* p) {
    print(p); }
__cdecl void foo3(int* p, unique_ptr<int> u) {
    print(p); }
__cdecl void bar3(unique_ptr<int> u, int* p) {
    print(p); }
int main() {
    unique_ptr<int> p(new int(1)), q(new int(2));
    foo(p.get(), move(p)); bar(move(q), q.get());
    unique_ptr<int> p2(new int(1)), q2(new int(2));
    foo2(p2.get(), move(p2)); bar2(move(q2), q2.get());
    unique_ptr<int> p3(new int(1)), q3(new int(2));
    foo3(p3.get(), move(p3)); bar3(move(q3), q3.get());
}

Output:

null
valid
null
valid
null
valid

Surprise is that I could not force it to change the order even when I used __stdcall and __cdecl .

EDIT : Same test with MSVC 2012 (__stdcall/__cdecl moved before names), same result !

Calling std::move() on arguments is perfectly safe.

What is not safe is tripping yourself up by writing to a raw pointer that was managed by an object that has relinquished the pointer's memory to the free store.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM