5

我正在阅读这个 stackoverflow 答案const T&&,其中给出了不是通用(转发)参考的一个原因:

允许 const T&& 充当转发引用,将无法重载仅将右值引用作为参数的模板函数。

我不知道这是什么意思。我猜这意味着同一函数(模板)有两个重载,其中一个作为参数const T&&。我还假设将始终调用其中一个重载,而永远不会调用另一个。

如果我的假设是正确的,那两个重载函数是什么?或者如果我错了,引用的段落实际上是什么意思?

谢谢你。

4

1 回答 1

4

据我所知,您引用的答案部分是准确的,但具有误导性。

首先,重要的是要澄清右值引用转发引用不是一回事,它们只是共享相同的符号&&。这是否是一件好事还有待商榷。

template <typename T>
void foo(T&&); // deduced type == forwarding reference

void foo(int&&); // explicit type == rvalue reference

很简单。那么为什么以下不是转发参考

template <typename T>
void foo(const T&&); // const rvalue reference despite deduced type

我能给你的最好答案是“因为”。这似乎是标准委员会的一个完全武断的决定。我看不出为什么const T&& 不能成为转发参考;这不是因为标准是这样说的。

§14.8.2.1/从函数调用中推导出模板参数 [temp.deduct.call]

转发引用是对 cv 非限定模板参数的右值引用。

不管为什么会这样,很明显,添加 cv-qualification 是告诉编译器将推导类型视为右值引用而不是转发引用的唯一方法。这是您从另一个答案中引用的观点。

允许 const T&& 充当转发引用,将无法重载仅将右值引用作为参数的模板函数。

我说这是误导的原因是因为它意味着如果我们重载模板以接受,那么无论 cv 限定如何,所有右值引用const T&&都将首选此重载。不是这种情况。

在下面的代码中,我们可以看到它bar接受const rvalue 引用,但没有其他内容,因为val它不是转发引用

struct Non_POD
{
    Non_POD(int i) : m_i(i) { }
    int m_i;
};

Non_POD foo() { return {0}; }

const Non_POD const_foo() { return {0}; }

template <typename T>
void bar(const T&& val)
{
    std::cout << "Accepts: const rvalue ref. ";
    if constexpr (std::is_rvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is rvalue reference.\n";
    }
    else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is lvalue reference.\n";
    }
    else
    {
        std::cout << "Val is lvalue.\n";
    }

    std::cout << std::endl;
}

int main()
{
    bar(foo());
    bar(const_foo());
    Non_POD x(0);
    //bar(x); // error
}

预期输出(GCC 7.1)

接受:const rvalue ref。Val 是右值引用。

接受:const rvalue ref。Val 是右值引用。

这似乎支持引用,因为bar接受const rvalue 引用并将rvalue 引用转换为const rvalue 引用。但是没有超载发生。如果我们引入重载,我们可以看到bar它只接受const rvalue 引用

struct Non_POD
{
    Non_POD(int i) : m_i(i) { }
    int m_i;
};

Non_POD foo() { return {0}; }

const Non_POD const_foo() { return {0}; }

template <typename T>
void bar(const T&& val)
{
    std::cout << "Accepts: const rvalue ref. ";
    if constexpr (std::is_rvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is rvalue reference.\n";
    }
    else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is lvalue reference.\n";
    }
    else
    {
        std::cout << "Val is lvalue.\n";
    }

    std::cout << std::endl;
}

template <typename T>
void bar(T&& val)
{
    std::cout << "Accepts: forwarding ref. ";
    if constexpr (std::is_rvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is rvalue reference.\n";
    }
    else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
    {
        std::cout << "Val is lvalue reference.\n";
    }
    else
    {
        std::cout << "Val is lvalue.\n";
    }

    std::cout << std::endl;
}

int main()
{
    Non_POD x(0);
    const Non_POD cx(0);

    bar(x);
    bar(cx);
    bar(Non_POD(0));
    bar(foo());
    bar(const_foo());
}

预期输出(GCC 7.1)

接受:转发参考。Val 是左值参考。

接受:转发参考。Val 是左值参考。

接受:转发参考。Val 是右值引用。

接受:转发参考。Val 是右值引用。

接受:const rvalue ref。Val 是右值引用。

从上面我们可以看出,实际上没有办法声明一个接受非 const 右值引用的模板。

于 2018-07-03T17:50:15.697 回答