Forward variadic template args to several class members
c++, c++11, perfect-forwarding, templates
Solution
You are going to want to use something like this:
#include <memory>
#include <string>
#include <iostream>
#include <utility>
template <typename T>
class Test
{
public:
template <typename... Args>
Test(Args&&... args)
: m_one(new T(args...)), // avoid moving the first time
m_two(new T(std::forward<Args>(args)...)) // but allowing moving the last time
{
}
private:
std::unique_ptr<T> m_one;
std::unique_ptr<T> m_two;
};
class C
{
public:
C(int a, int b, std::string c) // rule of thumb -- if you are going to copy an argument
// anyway, pass it by value.
: m_a(a),
m_b(b),
m_c(std::move(c)) // you can safely move here since it is the last use.
{
std::cout << "ctor a=" << m_a << ", b=" << m_b << ", c=" << m_c << "\n";
}
int m_a;
int m_b;
std::string m_c;
};
For `m_one`, the arguments use lvalue references, so no moving will take place. For `m_two`, the `std::forward` will use rvalue references as appropriate. Taking the `std::string` argument to `C` by value and using `std::move` makes it work properly for either case. If you pass an lvalue reference, then the argument will be copy-constructed, but if you pass an rvalue reference, the argument will be move-constructed. In either case, you can move the argument into your `m_c` member for efficiency.
Problem
Is the following safe? Won't the `std::string` be `move`d after the first class member is initialised? It prints out ok but I'm not sure. ``` template <typename T> class Test { public: template <typename... Args> Test(Args&&... args) : m_one(new T(std::forward<Args>(args)...)), m_two(new T(std::forward<Args>(args)...)) // <- Here { } private: std::unique_ptr<T> m_one; std::unique_ptr<T> m_two; }; class C { public: C(int a, int b, const std::string& c) : m_a(a), m_b(b), m_c(c) { std::cout << "ctor a=" << m_a << ", b=" << m_b << ", c=" << m_c << "\n"; } int m_a; int m_b; std::string m_c; }; int main() { Test<C> t(1, 2, "3"); } ``` I guess it's ok since the third ctor param of `C` is `const std::string&`, but how do I prevent perfect forwarding in a class that takes an r-value ref, e.g. `C(int, int, std::string&&)` as then `m_two` will not receive the same ctor args as `m_one`? Changing the ctor of Test to ``` template <typename... Args> Test(Args&... args) ``` doesn't compile. Nor does removing the `std::forward<Args>(args)...` from `m_one` and `m_two` ctors.