【问题标题】:Perfect Forwarding to a Private Class Constructor Through a Templated Friend Class通过模板化的朋友类完美转发到私有类构造函数
【发布时间】:2017-03-26 22:07:29
【问题描述】:

我正在探索将 friend 关键字与可变参数模板、完美转发和私有构造函数一起使用。我觉得我错过了所有这一切的基本内容,因为下面的简单示例无法编译。

我希望Test_Manager<Test_Class>::Process 成为构造Test_Class 类型对象的唯一方法(Process 最终会做得更多,但这是一个微不足道的例子)。我还希望Test_Manager 能够以这种方式“管理”各种类,因此类类型的参数t_Symbol 和可变参数...t_Args 来处理各种构造函数。

// Test.cpp
#include <string>

template<typename t_Symbol>
struct Test_Manager
{
    template<typename... t_Args>
    static t_Symbol Process(const t_Args&... i_Args)
    {
        const t_Symbol New_Symbol(std::forward<t_Args>(i_Args)...); // error C2665

        return New_Symbol;
    }
};

class Test_Class
{
    private:

        friend Test_Manager<Test_Class>;

        Test_Class() {};
        Test_Class(const std::string& i_Text) : m_Text(i_Text) {};

        const std::string m_Text;
};

void Test_Function()
{
    std::string text = "hello_world";

    Test_Class t = Test_Manager<Test_Class>::Process(text);
}

但是,在 Visual Studio 2015 Update 3 中,我收到以下错误(在上面标记的行):error C2665: 'std::forward': none of the 2 overloads could convert all the argument types。我在这里搞砸什么?我觉得这应该可行。

【问题讨论】:

    标签: c++ variadic-templates perfect-forwarding


    【解决方案1】:

    我在这里搞砸了什么?

    是的,你是。在下面的代码中:

    template<typename... t_Args>
    static t_Symbol Process(const t_Args&... i_Args){
        const t_Symbol New_Symbol(std::forward<t_Args>(i_Args)...);
        ...
    }
    

    上面的问题是i_Args 将有一个const 资格,但你告诉std::forward std::forward&lt;t_Arg&gt; 你将发送一个t_Arg 类型,它不带有const 资格。

    所以,本质上,您的std::forward&lt;t_Args&gt;(i_Arg) 的问题在于类型 t_Args 不带有隐式const,而是参数 @ 987654332@,有一个const

    您应该在转发引用上使用std::forward。当你这样做时,t_Arg 将隐式携带必要的 cv 限定条件,这将匹配 i_Arg

    cv 限定条件

    你想把它改成:

    template<typename... t_Args>
    static t_Symbol Process(t_Args&&... i_Args){
        ...
    }
    

    编辑,根据 Guillaume Racicot 的评论:

    当您尝试将值移动Process中时,例如:

    Test_Manager<Test_Class>::Process(std::move(text));
    

    Test_Class 的构造函数将击败 std::forward,因为它仍将按照原样进行复制。

    Test_Class(const std::string& i_Text) : m_Text(i_Text) {};
    

    但是,当构造函数定义为:

    Test_Class(std::string i_Text) : m_Text(std::move(i_Text)) {};
    

    不会复制,只会移动。

    【讨论】:

    • 可能会提到Test_Class 应该按值获取字符串并将其移动到成员而不是复制它。
    • 您能否详细说明一下为什么第一个构造函数会复制/击败 std::forward?
    【解决方案2】:

    std::forward 用于转发引用(或通用引用),这是一个T&amp;&amp; 函数参数,其中T 是一个模板参数。完美转发看起来像:

    template<typename... t_Args>
    static t_Symbol Process(t_Args&&... i_Args)
    {
        t_Symbol New_Symbol(std::forward<t_Args>(i_Args)...);
    
        return New_Symbol;
    }
    

    如果你想让Process 只取const 左值,那没关系,但是不要使用std::forward

    template<typename... t_Args>
    static t_Symbol Process(const t_Args&... i_Args)
    {
        t_Symbol New_Symbol(i_Args...);
    
        return New_Symbol;
    }
    

    【讨论】:

    • 啊...所以由于std::forward中涉及移动语义,它的参数不能是const lvalues?
    • 只要模板参数也是常量,参数可以是常量左值。在您的示例中,t_Argsstd::stringi_Argsconst std::string&amp;
    猜你喜欢
    • 2011-02-18
    • 2022-07-22
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 1970-01-01
    • 2021-08-16
    • 2014-07-31
    • 1970-01-01
    相关资源
    最近更新 更多