c++c++11rvalue-referencetemplate-classes

Conditional use of std::move


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?


Solution

  • 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;
    };