2

Consider the following code, which tries to move-construct a shared_ptr, but due to a mistake appears to copy-construct it:

#include <utility>
#include <cassert>
#include <memory>

int main()
{
    const auto x=std::make_shared<int>(4325); // can't be moved from
    const std::shared_ptr<int> y(std::move(x)); // silently copy-constructs
    assert(x==nullptr); // fails
}

Here the fact that due to x being const, y was copy-constructed instead of move-construction will only be detected at runtime. Is there any way to make sure move really occurs, at compile time?

Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • Why do you absolutely need `x` to be moved from? The copy will increment the reference counter and the destruction will decrement it. This would need to be some *really* hot code for this to make a difference. – TartanLlama Mar 04 '16 at 09:57
  • 1
    @TartanLlama the object may not necessarily be `shared_ptr`, it may be some heavy user-defined class. – Ruslan Mar 04 '16 at 09:59
  • See here: http://stackoverflow.com/a/28595207/4181011 – Simon Kraemer Mar 04 '16 at 10:02
  • @Ruslan So essentially you want to check if some expression is an rvalue and the type of the result is non-const? – TartanLlama Mar 04 '16 at 10:04
  • I thought about writing an answer, yet the one of @TartanLlama already shows a possibility. Still I want to share my description of what's happening here: `std::move&>(x)` will actually return a const rvalue reference to `std::shared_ptr` (`const std::shared_ptr&&`). Since `std::shared_ptr` doesn't have a appropriate constructor the value will be cast into a const reference (`const std::shared_ptr&`) which will call the copy constructor. – Simon Kraemer Mar 04 '16 at 10:15

1 Answers1

3

You could write a checker to see if an expression could be moved from:

template <typename T>
constexpr bool can_be_moved (T&&) {
    return !std::is_reference<T>{} && !std::is_const<T>{};    
}

Then you can static_assert that std::move(x) can be moved from:

int main()
{
    const auto x=std::make_shared<int>(4325);
    const std::shared_ptr<int> y(std::move(x));
    static_assert(can_be_moved(std::move(x)), "x must be able to be moved from");
}

I'm not really sure how useful this is in practice though. You still can't guarantee that something will truly be moved from unless you know what the constructors of the class do.

TartanLlama
  • 63,752
  • 13
  • 157
  • 193