Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::move in constructor initializer list in class template

I have a template like this:

template<typename T>
struct foo {
  T m_t;
  foo(T t) : m_t(t) {}
};

The problem is that I want to support both small/regular types and huge types (like matrices) for T. Do you recommend I write the constructor initializer list like this

foo (T t) : m_t(std::move(t)) {}

and require that the type T always support move construction, even for smaller types? Are there better ways?

like image 530
7cows Avatar asked Jun 10 '13 22:06

7cows


2 Answers

and require that the type T always support move construction, even for smaller types?

Any type that is copy constructable is also move constructable. Moving in those cases simply calls the copy constructor. Thus, there's no reason not to use m_t(std::move(t)).

An alternative is to use references instead:

foo (T const& t) : m_t(t) {}
foo (T&& t) : m_t(std::move(t)) {}

This has the benefit of only involving one construction rather than two.

like image 81
Pubby Avatar answered Oct 29 '22 00:10

Pubby


Yes, using the move has no disadvantage in that situation. All copyable objects are automatically moveable, so it doesn't matter. In fact, some recommend to always move variables when possible, even integers.

As an alternative, you may consider to use perfect forwarding, as described in this answer:

template <typename T2>
foo(T2&& t) : m_t(std::forward<T2>(t)) {}

If you know that T defines a fast move constructor, it should not matter. Otherwise, providing a constructor foo(const T&) is recommended to avoid unnecessary copies.

Perfect forwarding is only one technique to achieve that. The solution of Pubby to write out the constructor foo(const T&) and foo(T&&) is, of course, also fine. The results are the same, it is mostly a matter of style.

You also asked about small integer types. In theory, passing them by reference is slower than copying them, but the compiler should be able to optimize it to a copy, anyway. I don't think it will make a difference.

So, better optimize for the worst case where T can be huge and does not provide a fast move constructor. Passing by reference is best for that situation and should not be a bad choice in general, either.

like image 30
Philipp Claßen Avatar answered Oct 29 '22 01:10

Philipp Claßen