通过模板化朋友 Class 完美转发到私有 Class 构造函数

Perfect Forwarding to a Private Class Constructor Through a Templated Friend Class

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

我希望 Test_Manager<Test_Class>::Process 是构造类型 Test_Class 对象的唯一方法(Process 最终会做更多,但这是一个微不足道的例子)。我还希望 Test_Manager 能够以这种方式 'manage' 各种 classes,因此 class 类型的参数 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。我在这里搞砸了什么?我觉得这应该行得通。

std::forward 用于转发引用(或通用引用),它是一个 T&& 函数参数,其中 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;
}

What am I screwing up here?

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

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<t_Arg> 你会发送 t_Arg 类型,这不会' 具有 const 资格。

所以,从本质上讲,您的 std::forward<t_Args>(i_Arg) 的问题在于 Type t_Args 没有隐含的 const,但是 Argument i_Arg 有一个 const.

您应该在转发引用时使用 std::forward。当你这样做时,t_Arg 将隐式携带必要的 cv 资格,这将匹配 i_Argcv 资格

您想将其更改为:

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

不会复制,只会移动