1

I'm not looking for a type trait for movable types, nor rules for automatic generation of move operations. What I'm looking for is a general guide to know if a given type is going to be moved or copied, or a way to figure it myself with testing.

In some cases, the moving operation is performed without the user ever noticing, for example:

void f(std::string) { ... }
void f_c(const std::string) { ... }

void g()
{
    f(std::string("Hello world!"));   // moved, isn't it?
    f("Hello world!");                // moved, isn't it?
    f_c(std::string("Hello world!")); // moved, isn't it?
    f_c("Hello world!");              // moved, isn't it?
}

Prior to C++11 the code above would incurr in std::string copy from a temporary value to the one passed to f and f_c, from C++11 onwards the std::basic_string provides a move constructor (see here (8)) and the temporary created is moved into the parameter passed to f and f_c.

Sometimes the users were trying to force the move semantics with the std::move function:

std::string return_by_value_1()
{
    std::string result("result);
    return std::move(result); // Is this moved or not?
}

std::string return_by_value_2()
{
    return std::move(std::string("result)); // Is this moved or not?
}

But std::move doesn't move anything1: it only converts lvalues into rvalues references, if the target type doesn't implement move semantics: no move operation is performed... and AFAIK std::moveing in the return_by_value_x functions above prevents the compiler from doing RVO (we're worsening the code!).

So, after the (maybe unnecessary) introduction, I'll ask my questions:

How to know or test if a given type is going to be moved or copied?

This question is about basic types and complex ones:

int f_int(int) { ... };
template <typename F, typename S> void f_pair(std::pair<F, S>) { ... };

struct weird
{
    int i;
    float f;
    std::vector<double> vd;
    using complexmap = std::map<std::pair<std::string, std::uint64_t>, std::pair<std::uint32_t, std::uint32_t>>;
    complexmap cm;
};

struct silly
{
    std::vector<std::pair<const std::string, weird::complexmap>> vcm;
};

f_weird(weird) { ... };
f_silly(silly) { ... };

  • A basic type can be moved? there's some calls to f_int which implies a move operation?
    • f_int(1); // this moves or construct an int in-place?
    • f_int(1 + 2); // this moves or construct an int in-place?
    • f_int(f_int(1) + 2); // this moves or construct an int in-place?
  • A complex type with const members can't be moved, isn't it?
    • f_pair<std::pair<const std::string, int>>({"1", 2}); // unmovable?
    • f_pair<std::pair<std::string, std::string>>({"1", "2"}); // this moves?
    • f_silly({{{}, {}}}); // this moves?
  • The struct weird is moveable?
    • f_weird({1, .0f, {0.d, 1.d}, {{{"a", 0ull}, {1u, 2u}}}}) // this moves?
  • How to test by myself the above cases in order to determine if a given type is being moved in a given context?

1Wouldn't be better if it was called std::rvalref or something similar?

Community
  • 1
  • 1
PaperBirdMaster
  • 12,806
  • 9
  • 48
  • 94
  • 3
    ... you do realize that the signature of `f` and `f_c` is exactly the same? – Columbo Feb 12 '15 at 11:45
  • 2
    This appears to be a question in about 25 parts. Can you please ask one question per question? – Lightness Races in Orbit Feb 12 '15 at 11:48
  • I don't think there is a real question here. –  Feb 12 '15 at 11:55
  • Maybe nothrow constructable via rvalue? – Yakk - Adam Nevraumont Feb 12 '15 at 12:15
  • @LightnessRacesinOrbit Maybe you're right, in my mind all the questions are the same (all of them on the topic on move semantics) but maybe each part deserves an individual question, is this what are you suggesting? – PaperBirdMaster Feb 12 '15 at 12:26
  • @PaperBirdMaster: This is either one question that's too broad ("which types out of every type ever are moveable?") or it's 25 questions each repetitively asking "is X type moveable?" ;p – Lightness Races in Orbit Feb 12 '15 at 12:57
  • _"Wouldn't be better if it was called std::rvalref or something similar?"_ Yes, absolutely. The committee messed up on that one. They went waaaay too far down the naive "but it doesn't matter cos in practice people will only use it when they want to move, innit!" script-kiddie line of reasoning. – Lightness Races in Orbit Feb 12 '15 at 12:58

1 Answers1

1

How to test by myself the above cases in order to determine if a given type is being moved in a given context?

You can test whether a derived class would define a defaulted move constructor as deleted via SFINAE. This idea does not work for final classes. A rough sketch that seems to work is

namespace detail {
  // No copy constructor. Move constructor not deleted if T has an applicable one.
  template <typename T>
  struct Test : T { Test(Test&&) = default; };
}

// TODO: Implement unions
template <typename T>
using is_moveable = std::is_move_constructible<
    std::conditional_t<std::is_class<T>{}, detail::Test<T>, T>>;

Demo.

The move constructor of Test is a defaulted move constructor according to [dcl.fct.def.default]/5. The relevant quote that makes the above work is then in [class.copy]/11:

A defaulted [..] move constructor for a class X is defined as deleted (8.4.3) if X has:

  • a potentially constructed subobject type M (or array thereof) that cannot be [..] moved because overload resolution (13.3), as applied to M’s corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor, [..]

A defaulted move constructor that is defined as deleted is ignored by overload resolution (13.3, 13.4).

For the initialization

Test(Test())

to be valid, as the copy ctor is not implicitly declared, a move constructor must be available. However, according to the above quote, Test's move constructor will be defined as deleted (and thus ignored by overload resolution) if the base class, our given T, does not have an invokeable move constructor. (I am slightly unsure about the case where T does not have any move constructor, I'd be grateful for clarification on the standard there.)

Columbo
  • 60,038
  • 8
  • 155
  • 203