I'm trying to forward an r-value reference from a template class Foo
to another template class Bar
as follows:
#include <iostream>
#include <string>
#include <type_traits>
#include <utility>
template <typename... Ts>
struct Bar {
void do_something(Ts... things) {
std::cout << "Got " << sizeof...(Ts) << " things" << std::endl;
}
};
template <typename... Ts>
struct Foo {
void do_something(Ts... things) {
m_bar.do_something(things...);
}
Bar<Ts...> m_bar;
};
int main(int argc, char** argv) {
Foo<std::string&, std::string&&> foo;
std::string hello1("Hello");
std::string hello2("Howdy");
foo.do_something(hello1, std::move(hello2));
return 0;
}
However, when we get to Foo::do_something
, the 2nd argument needs to be moved again. I can't use std::move
here because that would also move the 1st argument, for example:
m_bar.do_something(std::move(things)...);
So, is there a way to move only the 2nd argument?
You could use a forwarding reference and perfect forwarding in order to copy the first argument and move the second in the second call:
template <typename... Ts>
struct Foo {
template <typename... Us>
void do_something(Us&&... things) {
// ^^^^
// forwarding reference
m_bar.do_something(std::forward<Us>(things)...);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
// copy first, move second
}
Bar<Ts...> m_bar;
};
Note that the references in Foo<std::string&, std::string&&> foo;
should most probably not be references here but it depends on how you use them later.
If that declaration is actually what you'd like to have, you don't have to make do_something
a function template but can reuse the Ts...
in the class template parameter list:
template <typename... Ts>
struct Foo {
void do_something(Ts... things) {
m_bar.do_something(std::forward<Ts>(things)...);
}
Bar<Ts...> m_bar;
};