C++ 为什么一个完美的转发功能必须被模板化?

C++ 为什么一个完美的转发功能必须被模板化?,c++,c++11,perfect-forwarding,C++,C++11,Perfect Forwarding,为什么以下代码有效: template<typename T1> void foo(T1 &&arg) { bar(std::forward<T1>(arg)); } std::string str = "Hello World"; foo(str); // Valid even though str is an lvalue foo(std::string("Hello World")); // Valid because literal is rva

为什么以下代码有效:

template<typename T1>
void foo(T1 &&arg) { bar(std::forward<T1>(arg)); }

std::string str = "Hello World";
foo(str); // Valid even though str is an lvalue
foo(std::string("Hello World")); // Valid because literal is rvalue
模板
voidfoo(T1&&arg){bar(std::forward(arg));}
std::string str=“Hello World”;
foo(str);//即使str是左值也有效
foo(std::string(“helloworld”);//有效,因为literal是右值
但不是:

void foo(std::string &&arg) { bar(std::forward<std::string>(arg)); }

std::string str = "Hello World";
foo(str); // Invalid, str is not convertible to an rvalue
foo(std::string("Hello World")); // Valid
voidfoo(std::string&&arg){bar(std::forward(arg));}
std::string str=“Hello World”;
foo(str);//无效,str不能转换为右值
foo(std::string(“helloworld”);//有效的
为什么示例2中的左值不能以与示例1中相同的方式得到解析

另外,为什么标准认为需要在std::forward中提供参数类型比简单推导它更重要?无论是哪种类型,简单的向前呼叫都是表示意图

如果这不是一个标准的东西,只是我的编译器,我使用的是msvc10,这可以解释糟糕的C++11支持

谢谢

编辑1:将文字“Hello World”更改为std::string(“Hello World”)以生成右值。

首先,要全面了解转发。(是的,我将大部分答案委托给其他人。)

总之,转发意味着左值保持左值,右值保持右值。你不能用一个单一的类型,所以你需要两个。因此,对于每个转发的参数,您需要该参数的两个版本,这需要函数总共2N个组合。您可以对函数的所有组合进行编码,但如果使用模板,则会根据需要为您生成这些不同的组合


如果您试图优化拷贝和移动,例如:

struct foo
{
    foo(const T& pX, const U& pY, const V& pZ) :
    x(pX),
    y(pY),
    z(pZ)
    {}

    foo(T&& pX, const U& pY, const V& pZ) :
    x(std::move(pX)),
    y(pY),
    z(pZ)
    {}

    // etc.? :(

    T x;
    U y;
    V z;
};
然后你应该停下来,这样做:

struct foo
{
    // these are either copy-constructed or move-constructed,
    // but after that they're all yours to move to wherever
    // (that is, either: copy->move, or move->move)
    foo(T pX, U pY, V pZ) :
    x(std::move(pX)),
    y(std::move(pY)),
    z(std::move(pZ))
    {}

    T x;
    U y;
    V z;
};

您只需要一个构造函数指南:如果您需要自己的数据副本,请在参数列表中复制该副本;这使您可以决定复制或向上移动到调用方和编译器。

在bar中会发生什么?编译并不意味着它必须工作。我认为应该分别是
void foo(T1&arg)
void foo(std::string&arg)
“Hello World”
不是一个右值,它是一个左值,类型为
const char[12]
@AJG85条中发生的事情并不重要表示右值引用。@GMan为什么“Hello World”是左值?这是不可转让的。我是不是错过了什么鬼鬼祟祟的东西?如果我将其更改为std::string(“Hello World”),这意味着什么?那么它肯定是一个右值。@AJG85您刚才描述的是
std::move
,所以第一个示例之所以有效,是因为T1实际上被解析为
void foo(const std::string&&arg)
的等价物,它使用引用演绎规则被简化为
void foo(const std::string&arg)
?它在示例2中失败,因为字符串没有左值重载?是否有定义模板函数的最佳实践,以使它至少相对明显地显示应该是什么类型?理想情况下,我正在寻找一种很好的方法来显式地使用该类型,同时避免2^N重载。@Mranz:准确地说。那你去干什么?转发和模板是用来转发的,你不需要知道类型。假设我有一个构造函数,它接受一个向量,比如
类(…一些参数…,向量项)
。理想情况下,我希望允许该参数使用移动语义或作为右值来避免复制。为了支持这一点,我必须将项目模板化,或者创建一个右值重载。如果我将其模板化,实际类型将在定义中丢失,并且实际参数必须由我的类的使用者通过读取头文件或一些注释来推断。感谢提供的信息。这很有道理。为什么不使用x(std::forward(pX))等呢?std::forward的作用是什么?