2013-11-27 76 views
4

以下安全吗?在第一个类成员初始化之后,std::string不会是move d吗?它打印出来确定,但我不确定。转发可变参数模板参数给几个类成员

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"); 
} 

我想这是确定自C第三构造函数参数是const std::string&,但如何防止一类需要一个R值裁判,例如完美转发C(int, int, std::string&&)那么m_two将不会收到与m_one相同的参数?

变化的测试的构造函数来

template <typename... Args> 
    Test(Args&... args) 

不能编译。也不从m_onem_two ctors中删除std::forward<Args>(args)...

+0

也许最好不要在初始化'm_one'时使用'std :: forward'。 –

+0

不能编译:main.cpp:31:error:无法绑定'std :: basic_string '左值为'std :: string && {aka std :: basic_string &&}' – James

+0

@James:您必须做某件事错误。沃恩的意思是[this](http://coliru.stacked-crooked.com/a/0fdc7ca0f433a18a)。经验法则是:当多次转发相同的参数时,***不要***。只需传递它,而不需要完美转发,并让目标制作副本,如果他们想要的话。如果你想转发,只能在最后一次(在这种情况下在'm_two'的初始化中)执行。 – Xeo

回答

7

你将要使用这样的事:

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

m_one,参数使用左值引用,所以没有移动会发生。对于m_twostd::forward将酌情使用右值引用。根据值将std::string参数转换为C,并使用std::move可使其适用于任一情况。如果你传递一个左值引用,那么这个参数将是复制构造的,但是如果你传递一个右值引用,则该参数将被移动构造。无论哪种情况,您都可以将参数移至您的m_c成员以提高效率。