0

The following code compiles and prints: move move. I would prefer that it didn't compile since merge takes rvalue references and I don't move t1 and t2 to it.

class A {
   public:
    A() = default;
    A(const A& other) { std::cout << "copy "; };
    A(A&& other) { std::cout << "move "; };
};

template <typename... TupleType>
auto merge(TupleType&&... tuples) {
    return std::tuple_cat(std::move(tuples)...);
}
int main() {
    std::tuple<int> t1{1};
    std::tuple<A> t2{A()};
    auto t3 = merge(t1, t2);
}

I'm not sure what happens here and why. Furthermore, I think this behavior is dangerous: I have no move in the call to merge, but t1 and t2 are moved from.

Why is this allowed and how can I make merge take only rvalue references?

Rudolf Lovrenčić
  • 147
  • 1
  • 2
  • 9
  • 6
    `TupleType&&` is a forwarding reference, not simply an rvalue-reference, due to reference collapsing rules. Use `std::forward(tuples)...` instead of `std::move(tuples)...` and you'll get what I expect is the behaviour you desire. See [this Q/A](https://stackoverflow.com/questions/7257144/when-to-use-stdforward-to-forward-arguments) and [this Q/A](https://stackoverflow.com/questions/28828159/usage-of-stdforward-vs-stdmove) – alter_igel Jul 23 '20 at 20:35
  • 1
    @alterigel Answer should belong to answer-box! – UserUsing Jul 23 '20 at 21:43

1 Answers1

3

For why is this possible, See Reference_collapsing.

Now If you want to prevent your function from accepting lvalues, you can use the following code

#include <tuple>
#include <iostream>


class A {
public:
    A()  = default;
    A(const A& other) { std::cout << "\ncopy "; }
    A(A&& other)noexcept { std::cout << "\nmove "; }
};
template <typename... TupleType>
auto merge(TupleType&... tuples) = delete;

template <typename... TupleType>
auto merge(TupleType&&... tuples) {
    return std::tuple_cat(std::forward<TupleType>(tuples)...);
}

int main() {
    std::tuple<int> t1{1};
    std::tuple<A> t2{A()};
    // auto t3 = merge(t1, t2);//won't compile

    //compiles and gives the desired behavior move move
    auto t4 = merge(std::make_tuple(1), std::make_tuple(A{}));
}

Live

asmmo
  • 6,922
  • 1
  • 11
  • 25