为什么一个完美的转发功能一定要模板化?

问题描述 投票:0回答:1

为什么以下代码有效:

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

但不是:

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

为什么示例 2 中的左值没有以与示例 1 中相同的方式解析?

此外,为什么标准认为需要在

std::forward<T>
中提供参数类型而不是简单地推导它很重要?无论类型如何,只需拨打
forward
即可表明意图。

如果这不是标准的东西而只是我的编译器,那么我使用的是 msvc10,这可以解释蹩脚的 C++11 支持。

编辑 1:将文字

"Hello World"
更改为
std::string("Hello World")
以创建右值。

c++ c++11 perfect-forwarding
1个回答
16
投票

首先,阅读本文以全面了解转发。 (是的,我将大部分答案委托给其他地方。)

总而言之,转发意味着左值保持左值,右值保持右值。您无法使用单一类型来做到这一点,因此您需要两种。因此,对于每个转发的参数,您需要该参数的两个版本,这需要该函数总共 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;
};

你只需要一个构造函数。 指南:如果您需要自己的数据副本,请在参数列表中制作该副本;这使得调用者和编译器能够决定复制或移动。

© www.soinside.com 2019 - 2024. All rights reserved.