Return local value from function without triggering copy constructor
Asked Answered
T

2

7

I am attempting to delete the copy constructor using the c++ type system to prevent copying an object.

struct DeleteCopyConstructor {
    DeleteCopyConstructor() {};
    DeleteCopyConstructor(DeleteCopyConstructor& op2) = delete;
    DeleteCopyConstructor(const DeleteCopyConstructor& op2) = delete;
};

DeleteCopyConstructor f() {
    DeleteCopyConstructor d;
    // initialize d...
    return d;
}

The error is:

error: use of deleted function ‘DeleteCopyConstructor::DeleteCopyConstructor(const DeleteCopyConstructor&)’

I've read about copy elision, but it appears to be a compiler optimization, so I don't think it applies. How can I return d without triggering copy construction?

Tomchay answered 25/6, 2017 at 20:56 Comment(6)
Copy elision (named return value optimization or NRVO) would apply in this case. And it is likely your compiler implements it.Tombstone
Is moving it off the table?Pediment
BTW you don't need to delete two copy constructors.Tombstone
Is the copy constructor deleted to prevent dangerous/illogical copies?Pediment
Asked on freenode IRC. The code compiles if I provide move constructor. DeleteCopyConstructor(DeleteCopyConstructor&& op2) = default;Tomchay
One viable possibility is to create a char[sizeof DeleteCopyConstructor] variable on the stack of the caller, pass the pointer to f as an out variable, and use a placement new.Tomchay
K
2

In C++17 the compiler is guaranteed to elide the copy. However in all cases you still need to have a valid copy constructor.

So if you're just concerned about performance then you don't have to do anything. If you want to delete the copy constructor because the value logically shouldn't be copyable then there is no way to do it as far as I know. You'd have to return a std::unique_ptr<T> or take the value by reference and move into it that way. Edit: Or define a move constructor.

Kannan answered 24/9, 2018 at 14:20 Comment(4)
std::unique_ptr is a very good suggestion (+1), but that's still too much indirection - one must suppose the goal is to initialize a value that exists on the stack of the caller. :)Tomchay
Yeah the only way I know to do that exactly is with placement new, but it will get very ugly. Move constructor is probably the next best option.Kannan
Because the compiler is now guaranteed to elide the copy or move, you can just declare a copy or move constructor and leave it unimplemented. This way, you can return by value, but legitimate copies/moves will generate a linker error.Burgin
Named return value optimization is not guaranteed in C++17. For the guaranteed copy elisions the copy/move operator also doesn't need to be accessible/usable since C++17.Clubman
V
-1

Copy elision, but it appears to be a compiler optimization, so I don't think it applies.

It does. Read more in Move or Named Return Value Optimization (NRVO)?

How can I return d without triggering copy construction?

Let the compiler take care of it.

Voltameter answered 25/6, 2017 at 20:59 Comment(4)
Well, what I'm trying to do is lean on the type system to ensure correct code. I want copying an object to be a type error. Because copy elision is an optimization, and works in spite of language semantics saying the variable should be copied, it would not suit my needs in this case. What I have taken to doing, is passing "out" parameters by ref, and "in" parameters by const ref - which is not perfect, but is working okay, and eliminates any need to return a value from a function without semantically copying it.Tomchay
I see @jcarpenter. Why you didn't accept my answer? You want me to modify it according your comment? I would say to keep it that way and leave your comment here.Voltameter
I actually triggered a situation where NRVO didn't trigger although it is almost as simple as your above case.Priebe
@Voltameter because it does not answer the question. I want to lean on the type system hereTomchay

© 2022 - 2024 — McMap. All rights reserved.