For setters that set a member collection, I write template functions so as to accept any collection type as input:
class HasItems
{
public:
template <typename C>
void items(const C& items) {
// Copy-and-swap omitted
m_items.assign(std::begin(items), std::end(items));
}
private:
std::vector<Item> m_items;
}
But suppose Item
is big, and I want to move them around instead of copy if possible. And furthermore, suppose someone passed me an xvalue std::vector<Item>
; I could just move that whole container into place and avoid touching the elements at all! That looks like this:
class HasItems
{
public:
template <typename C>
void items(C&& items) {
// Helper function implemented below
assign(m_items, std::forward<C>(items));
}
private:
std::vector<Item> m_items;
}
// Assigns the contents of src to dest, moving as much as possible
template <typename C1, typename C2>
void assign(C1& dest, C2&& src) {
assign(dest, std::forward<C2>(src), std::is_assignable<C1&, C2&&>{});
}
// Called if we can use operator=
template <typename C1, typename C2>
void assign(C1& dest, C2&& src, std::true_type use_equals) {
dest = std::forward<C2>(src);
}
// Called if we must use dest.assign()
template <typename C1, typename C2>
void assign(C1& dest, C2&& src, std::false_type use_equals) {
assign_elements(dest, std::forward<C2>(src), std::is_rvalue_reference<C2&&>{});
}
// Called if we can move the elements
template <typename C1, typename C2>
void assign_elements(C1& dest, C2&& src, std::true_type move) {
dest.assign(std::make_move_iterator(std::begin(src)),
std::make_move_iterator(std::end(src)));
}
// Called if we must copy the elements
template <typename C1, typename C2>
void assign_elements(C1& dest, C2&& src, std::false_type move) {
dest.assign(std::begin(src), std::end(src));
}
Is this a good approach? In particular, is there a nice way to avoid the tag dispatch on std::is_rvalue_reference<C2&&>
in the assign_elements()
case? (It was avoided in the operator=
case by using dest = std::forward<C2>(src)
instead of using std::move()
conditionally.) It seems to pass my simple set of unit tests.