std::move for pointer?

I came across some strange code while reading a textbook (C++ Templates: The Complete Guide 2e) today that doesn't exactly make sense to me.

1
2
3
4
5
6
7
8
9
10
11
12
13

template<typename T>
class Stack {
  private:
    std::vector<T> elems;
  // elements
  public:
    Stack (T elem)
    // initialize stack with one element by value
    : elems({std::move(elem)}) {}
};

Stack stringStack = "bottom"; // Stack<char const*> deduced since C++17 


So in the following code, the constant "bottom" is deduced as a const char* since it is passed by value in the template as opposed to by reference. However, in the ctor-initializer, the value is casted to an rvalue reference and is moved into the vector.

The textbook originally had this implemented without the std::move, but stated that it would be better to std::move elem into the vector to avoid unnecessary copying.

However, this doesn't really make sense to me for const char*. Since it is a pointer, there is really no advantage to using move semantics versus value semantics because the actual data is never copied, only the pointer is. Furthermore, the data itself is const-qualified and is stored in eprom. So why does the author suggest to std::move() this value?
Last edited on
> So why does the author suggest to std::move() this value?

It is a template; in some instaniation of the template, the type T may have an efficient move constructor.


> but stated that it would be better to std::move elem into the vector to avoid unnecessary copying.

However, using the initialiser list constructor would make a copy anyway.
(First move into the initialiser list, then copy from the initialiser list; the inilialiser list is a list of const lvalues).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <vector>

struct A
{
    A() = default ;
    A( const A& ) { std::cout << "copy construct\n" ; }
    A( A&& ) { std::cout << "move construct\n" ; }
    A& operator= ( const A& ) = default ;
    A& operator= ( A&& ) = default ;
    ~A() = default ;
};

template < typename T > struct foo
{
    std::vector<T> vec ;
    foo( T v ) : vec( { std::move(v) } ) {} // copy from initialiser list
};

template < typename T > struct bar
{
    std::vector<T> vec ;
    bar( T v ) { vec.push_back( std::move(v) ) ; } // push back rvalue
};

int main()
{
    std::cout << "foo\n" ;
    // move construct into the initialiser list, then copy the moved item
    foo<A> f( A{} ) ;
    // foo
    // move construct
    // copy construct

    std::cout << "\nbar\n" ;
    // move construct into the vector
    bar<A> b( A{} ) ;
    // bar
    // move construct
}

http://coliru.stacked-crooked.com/a/f898653b063ca9b2
BTW, it's definitely NOT stored in eprom! It MAY be stored in memory that has been marked read-only by the OS, but that's not an EPROM. (Actually, I guess it could be stored in an EPROM on an embedded system.)
So the initializer list constructor would make a copy anyway? Then why does the author use std::move in the initializer list if it will make a copy anyway?
> Then why does the author use std::move in the initializer list if it will make a copy anyway?

In order to avoid copying twice.
The question is why is he using the initializer list at all?
So the initializer list constructor would make a copy anyway? Then why does the author use std::move in the initializer list if it will make a copy anyway?

One copy is still avoided.

The object is moved into an element of the initializer_list but copied from the initializer_list into the vector. The copy occurs because elements of an initializer_list cannot be moved-from, because generally a move-operation modifies the moved-from object, and initializer_list stores objects of type const T.

See lines 20-24 of JLBorges's code for an example of how to avoid unnecessary copies inside the constructor.
Last edited on
Topic archived. No new replies allowed.