2

A friend of mine stumbled on a question

Is there any way to return a copy of an object which is copy-able but NOT move-able. In other words, can we make the following code work?

struct A {
    A() = default;
    A(A const&) = default; // copyable
    A& operator=(A const&) = default; // assignable
    A(A &&) = delete; // not movable
    A& operator=(A &&) = delete; // not movable
    ~A() = default;
};

A foo() {
     A a;
     return a;
}

int main() {
    A a(foo()); //will fail, because it calls A(A&&)
    return 0;
}

In my opinion, we can't, because foo() is an A&& an then the compiler has to call A(A&&). But I would like some kind of confirmation.

Niall
  • 30,036
  • 10
  • 99
  • 142
Davidbrcz
  • 2,335
  • 18
  • 27
  • 6
    If you declare the copy ctor and copy assignment operator, then the compiler won't generate the implicit move ones. And if you then declare the move members deleted, you are telling it "I want to make assigning/constructing from a temporary illegal, not use the copy constructor", and the compiler will happily oblige. – T.C. Jul 22 '14 at 19:38
  • 1
    Related: http://stackoverflow.com/q/24867873 – dyp Jul 22 '14 at 20:29

2 Answers2

3

Following on from the code in the posted comment by dyp; Is there a cast (or standard function) with the opposite effect to std::move?, the following code snippet compiles.

I'm not sure on the usefulness of the code, there was some discussion on whether this was a good idea or not in that linked post.

struct A {
    A() = default;
    A(A const&) = default; // copyable
    A& operator=(A const&) = default; // assignable
    A(A &&) = delete; // not movable
    A& operator=(A &&) = delete; // not movable
    ~A() = default;
};

template <typename T>
T& as_lvalue_ref(T&& t) {
    return t;
}

A foo() {
     A a;
     return as_lvalue_ref(a);
}

int main() {
    A a(as_lvalue_ref(foo()));
    return 0;
}

Code sample here.

A static_cast<T&>() would also work in place of the as_lvalue_ref() and may even be preferable (although looking clumsy) given that it is in fact verbose. In any situation where a reference is returned, dangling references can happen. Given the signature A foo() in this case (instead of say A& foo()), there are no dangling references here, a full object of A returned. The line A a(as_lvalue(foo())); has no dangling references either since the temporary (prvalue) returned from as_lvalue_ref(foo()) remains valid until the end of the expression (;) and then the a object will be then well formed.

Community
  • 1
  • 1
Niall
  • 30,036
  • 10
  • 99
  • 142
0

Someone came up in another forum with that code. It should not lead to potential dangling references, which may be the case with Niall's code. But is still convoluted for something that was legal in C++03

#include <iostream>
#include <functional>

struct A {
    A() =default;
    A(A const& a)=default;
    A& operator=(A const& a)=default;
    A(A &&) = delete; // not movable
    A& operator=(A &&) = delete; // not movable
    ~A()=default;
    int n;
};

A foo(void) {
    A a;
    a.n=5;
    std::cout<<"&a="<<&a<<std::endl;
    return std::cref(a); // A(A const&)
    // ~A()
}

int main(void) {
    A b; // A()
    std::cout<<"&b="<<&b<<std::endl;
    A const& const_ref_to_temp=foo(); // OK in C++
    b= const_ref_to_temp; //A=A const& [OK]
    std::cout<< b.n<<std::endl;
    return 0;
}
Davidbrcz
  • 2,335
  • 18
  • 27
  • I note the `std::cref`, it's a neat alternative. Essentially all solutions that start "forwarding" references to values could lead to dangling references. If used correctly though, both these techniques would work. Even a `static_cast()` could work... although I think the cast may look clumsy. – Niall Jul 25 '14 at 13:02