【问题标题】:C++: const not allowed in universal (forwarding) referenceC++:通用(转发)参考中不允许使用 const
【发布时间】:2018-12-11 03:34:01
【问题描述】:

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

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

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

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

谢谢。

【问题讨论】:

  • 为什么人们用 cmets 回答而不是回答?
  • @0x5453 你当然可以有 const 右值引用
  • @0x5453 在我链接到使用 const rvalue 引用的同一答案中提到,因此它们是允许的。
  • 写一个好的答案比写评论需要更多的努力,因为一个人必须确保他的答案是充分的(并且足够完整),这样他就不会被否决。
  • @ChristopherPisz:因为如果 cmets 错了,他们就不能投反对票。 Exhibit A 是 0x5453 的评论。

标签: c++ forwarding-reference


【解决方案1】:

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

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

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&amp;&amp; 不能成为一个转发参考;这不是因为标准是这样说的。

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

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

不管为什么会出现这种情况,很明显,添加 cv 限定是告诉编译器将推导的类型视为 右值引用的唯一方法而不是转发参考。您从其他答案中引用的内容是什么意思。

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

我说这是误导的原因是因为它暗示如果我们重载模板以接受const T&amp;&amp;,那么无论所有 右值引用都将首选此重载简历资格。事实并非如此。

在下面的代码中,我们可以看到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 references 并将 rvalue references 转换为 const rvalue references。但是没有超载发生。如果我们引入重载,我们可以看到 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右值引用的模板。

【讨论】:

  • 我在关于我的问题的最后一条评论中得出了相同的结论:“如果引用的答案试图说如果 T&& 和 const T&& 都是通用引用,那将是不可能的定义一个函数,该函数将采用任何类型的右值(但只有右值,而不是左值),因为如果两个版本都是通用引用,那么左值也将被接受?因此,鉴于 const T&& 不是通用引用。函数模板 f(const T&&) 将接受任何类型的右值(但不接受左值)。”。这就是你的意思,对吧?
  • 这就是我要说的。但是,如果您要重载,请注意声明接受通用/转发引用的重载将导致const T&amp;&amp; 接受 const 右值引用。万一您希望为右值引用(const 和非 const)提供一个重载,而另一个为其他所有内容提供完美转发,那么您无法使用当前语法来实现。
  • 是的,我明白了。我不知道为什么在我引用术语“重载”的答案中使用。也许作者试图说使用模板函数(只是一个重载)在某种程度上类似于“重载”该函数(而不是使用 C++ 重载的字面意思)。
  • 是的,我也不知道他为什么用它。很高兴能提供帮助。
猜你喜欢
  • 2020-12-13
  • 1970-01-01
  • 2017-03-03
  • 2017-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多