c++ - 当函数接受 T&& 的参数并传递左值时,模板参数 T 是否应该解析为 T&?

标签 c++ templates c++11 perfect-forwarding

<分区>

我正试图在 C++ 中解决完美转发问题,因此我编写了以下快速而肮脏的代码来测试它。

class CValue
{
public:

    CValue(int i) :
        m_i(i)
    {
        printf("Default constructor called!\r\n");
    }

    CValue(const CValue& src) :
        m_i(src.m_i)
    {
        printf("Copy constructor called!\r\n");
    }

    CValue(CValue&& src) :
        m_i(src.m_i)
    {
        printf("Move constructor called!\r\n");
    }

    CValue& operator=(const CValue& src)
    {
        m_i = src.m_i;
        printf("Copy assignment called!\r\n");
        return *this;
    }

    CValue& operator=(CValue&& src)
    {
        m_i = src.m_i;
        printf("Move assignment called!\r\n");
        return *this;
    }

    int     m_i;
};

template <typename T>
void PerfectForwarding(T&& tValue)
{
    T tValue1 = tValue;
    tValue1.m_i = 10;
}

int _tmain(int argc, _TCHAR* argv[])
{
    CValue v(0);
    PerfectForwarding(v);

    printf("%d\r\n", v.m_i);

    return 0;
}

当我构建并运行此代码作为控制台应用程序时,我得到了答案 10,而我期望的是 0。

这行好像是:

T tValue1 = tValue;

在 PerfectForwarding 函数中被解析为:

CValue& tValue1 = tValue;

代替:

CValue tValue1 = tValue;

所以编译器将 T 解析为 CValue&,这是我没想到的。

我尝试通过显式声明模板参数类型从 _tmain 调用函数,即

PerfectForwarding<CValue>(v);

但是编译失败并出现以下错误:

error C2664: 'void PerfectForwarding<CValue>(T &&)' : cannot convert
              argument 1 from 'CValue' to 'CValue &&'
              with
              [
                  T=CValue
              ]
You cannot bind an lvalue to an rvalue reference

我可以通过将 PerfectForwarding 函数中的行更改为以下内容来强制执行所需的行为:

typename std::remove_reference<T>::type tValue1 = tValue;

但我认为这不是必要的。根据引用折叠规则,参数类型 (T&&) 应该变成 CValue&(如 T&& & -> T&),但是 T 本身应该只是 CValue,对吧?这是 VC12 编译器处理右值引用的错误,还是我误解了右值引用和模板?

我在调试中使用 Visual Studio 2013(VC12 编译器)并关闭所有优化。

最佳答案

当编译器只看到 T&& 时它将尝试推断T到允许用你给它的任何东西调用函数的东西。所以你结束了 T正在Cvalue&当使用左值调用时,引用折叠(如您所指出的)可以开始。

当您尝试提出论点时,这很重要。 std::forward<T>(v)将参数转发为左值或右值取决于什么 T是。如果它被推导为左值引用,它将作为左值转发,如果没有,它将作为右值转发。类型T是唯一的区别。

根据引用折叠规则,参数类型 (T&&) 应该变为 CValue&(如 T&& & -> T&),但 T 本身应该只是 CValue,对吧?
如果用左值调用它会变为 (T&& v) “好吧,我不能将右值引用绑定(bind)到左值,但如果我让 T 本身成为左值引用,那么这就可以了。” T推导为 Cvalue&这样(T&& v)扩展为 (Cvalue& && v) .现在引用已折叠 (Cvalue& v) .类型T必须是左值引用类型才能工作。

如果您明确提供模板参数,那么您并没有真正解决问题。第一种解决方法是 remove_reference正如你所发现的。您也可以使用 auto这更有意义,因为这是泛型编程

auto tValue1 = tValue;

无论如何,auto不会推断出引用。对于您提供的内容,您最好使用 const可以绑定(bind)到左值和右值的左值引用。

template <typename T>
void PerfectForwarding(const T& tValue)
{
    T tValue1 = tValue;
    tValue1.m_i = 10;
}

这不允许转发,但无论如何它都不是转发功能。

如果你想以不同的方式处理左值,你可以提供两个重载

template <typename T>
void PerfectForwarding(T& tValue);

template <typename T>
void PerfectForwarding(T&& tValue);

当使用左值调用时,前者将是首选。

关于c++ - 当函数接受 T&& 的参数并传递左值时,模板参数 T 是否应该解析为 T&?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28028606/

相关文章:

编辑器中的 C++ 自动类实现

html - Angular 2在组件继承模板中包含组件html

c++ - QMetaEnum 和强类型枚举

c++ - 动态大小数组在哪里创建? (堆栈或堆)

c++ - 在模板函数中包含不变假设

javascript - 是否存在像 javascript es6 字符串模板文字这样的 Go 字符串模板?

c++ - 具有共享指针参数歧义的函数重载

c++ - 传递 std::shared_ptr 的 std::vector,不更新对象

c++ - 使用 OpenCV 进行图像分割

c++ - 类模板实例化