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

家住魔仙堡 提交于 2020-01-02 09:01:14

问题


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 ?


回答1:


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<




回答2:


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!




回答3:


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.



来源:https://stackoverflow.com/questions/25180339/c11-stdmove-call-on-arguments-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!