【问题标题】:Binding lvalue to rvalue reference -- g++ bug?将左值绑定到右值引用——g++ 错误?
【发布时间】:2012-10-14 10:06:58
【问题描述】:

作为对another question的回复,我想贴出如下代码(也就是我想贴出基于这个想法的代码):

#include <iostream>
#include <utility>      // std::is_same, std::enable_if
using namespace std;

template< class Type >
struct Boxed
{
    Type value;

    template< class Arg >
    Boxed(
        Arg const& v,
        typename enable_if< is_same< Type, Arg >::value, Arg >::type* = 0
        )
        : value( v )
    {
        wcout << "Generic!" << endl;
    }

    Boxed( Type&& v ): value( move( v ) )
    {
        wcout << "Rvalue!" << endl;
    }
};

void function( Boxed< int > v ) {}

int main()
{
    int i = 5;
    function( i );  //<- this is acceptable

    char c = 'a';
    function( c );  //<- I would NOT like this to compile
}

然而,虽然 MSVC 11.0 在最后一次调用中阻塞,正如 IHMO 应该的那样,MinGW g++ 4.7.1 只是接受它,并使用右值引用形式参数调用构造函数。

在我看来,它看起来就好像一个左值绑定到一个右值引用。一个灵活的答案可能是将左值转换为右值。但问题是,这是否是编译器错误,如果不是,神圣标准如何允许这样做?


编辑:我设法将其简化为以下非常简短的示例:

void foo( double&& ) {}

int main()
{
    char ch = '!';
    foo( ch );
}

用 MSVC 11.0 编译失败,用 MinGW 4.7.1 编译,对吗?

【问题讨论】:

  • 难道没有从 charint 的隐式转换(来自 C 我希望这样)?
  • @H2CO3:在 C++ 中,您可以重载 intchar 作为参数类型。所以,不,在这种情况下没有转换。或者,我看不到如何(正确)调用它。
  • @Cheersandhth.-Alf:你可以超载,但你仍然有自动投射。 IE。如果你有void function(char c) {},它会被收集,但是你从int &amp;&amp;构造Boxed&lt;int&gt;,你从char构造它(合法,因为它是r值,但如果你需要l值,它就不会)。
  • 无论如何,我无法想象为什么这个问题会被否决...
  • @H2CO3:我后面跟着一些连续投票者。他或她将其限制在足够低的频率,以至于它不会被 SO 机制自动修复。偶尔的连续投票者在一定程度上弥补了这一点。 :-)

标签: c++ compiler-errors


【解决方案1】:

我没有检查规范,但我猜 char 可以自动转换为 int。由于您不能分配任何东西(它是 r 值),因此将传递给 int 类型的临时变量(更明确地是 (int)c 值)的 R 值。

【讨论】:

  • 自此答案以来,我更新了问题以包含double&amp;&amp; 的示例。 char -> int 是与char -> double 不同类型的转换(标准中的一组不同规则)。所以我认为这个新例子澄清了一点:机制不可能是自动上转换为int
  • @Cheersandhth.-Alf:但是转换 char -> double 仍然是合法的,所以 (double)c 类型的值仍然是有效的 r 值。
【解决方案2】:

我发现 N3290(与 C++11 标准相同)包含将 double&amp;&amp; 绑定到从 int 左值生成的右值的非规范示例,以及 §8.5.3中的更新措辞>

“如果 T1 与 T2 引用相关并且引用是右值引用, 初始化表达式不应是左值。”

据报道,这些规则旨在避免低效的额外复制。虽然我看不出这种复制是如何被优化掉的。无论如何,不​​管这个理由是否合理——而且它肯定不会似乎作为一个合理的效果! – 允许使用以下代码,并且可以使用 MSVC 11 和 MinGW g++ 4.7 进行编译:

struct Foo {};
struct Bar { Bar( Foo ) {} };

void ugh( Bar&& ) {}

int main()
{
    Foo o;
    ugh( o );
}

显然 MSVC 11 不允许左值 -> 右值转换是错误的。


编辑:我了解到有关于此问题的缺陷报告,DR 1414。 2012 年 2 月的结论是,当前的行为规范是“正确的”,大概就其反映意图的程度而言。然而据报道,委员会仍在讨论它,大概是关于意图的实用性。

【讨论】:

  • 1) N3290 C++11标准,是经过国家机构投票并重新发布为标准的论文。
  • 2) 如果Bar b = o; 可以,为什么ugh( o ) 也可以?你得到一个到临时Bar 的隐式转换,并且右值引用可以绑定到临时。第一个等价于Bar b = Bar(o),第二个等价于ugh( Bar(o) ),除了允许隐式转换,Foo -> Bar 可以隐式转换,char -> double跨度>
  • @Jonathan:关于你的“为什么”,你在询问标准的基本原理。对于普通参考,主要的理由是它会打破对调用后更改值的期望。对于右值引用,仍然存在这种期望,尤其是。用于传入参数。还期望能够区分类型。因此,存在相同的理由。它导致了不同的规则,这在逻辑上意味着如果导致规则的过程是完美的,那么对右值的一些额外问题肯定在起作用,例如避免不必要的复制。
  • Re (1) 谢谢,我在新笔记本电脑上搞糊涂了。这是这里编号最高的草稿,我不记得编号了。谢谢,已修复!
【解决方案3】:

大概你同意这是有效的?

void foo( double ) {}  // pass-by-value

int main()
{
    char ch = '!';
    foo( ch );
}

有一个从chardouble的隐式转换,所以这个函数是可行的。

在您编辑的问题中的示例中是相同的,有一个隐式转换会产生一个临时(即右值)并且右值引用参数绑定到该临时。如果您愿意,可以明确地进行转换:

void foo( double&& ) {}  // pass-by-reference

int main()
{
    char ch = '!';
    foo( double(ch) );
}

但在这种情况下,这并没有真正改变任何东西。如果 double -> char 只能显式转换(例如,对于具有显式构造函数或显式转换运算符的类类型),但 doublechar 是有效的隐式转换,则这是必要的。

您正在考虑的“右值引用不能绑定到左值”规则是指将T&amp;&amp; 绑定到T 左值,并且该规则没有被破坏,因为double&amp;&amp; 没有绑定到char,它绑定到由隐式转换创建的临时。

该规则的存在不仅是为了防止不必要的额外复制,而且是为了解决以前规则中存在的真正安全问题,请参阅http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2812.html

编辑:有人询问委员会反射器是否需要这种行为(参见DR 1414),并决定是的,这种行为是有意的并且是正确的。用于达到该位置的参数之一是该代码在当前规则下更有效:

std::vector<std::string> v;
v.push_back("text");

根据当前规则,通过隐式转换创建一个临时的std::string,然后调用std::vector&lt;T&gt;::push_back(T&amp;&amp;),并将临时的移动到向量中。如果push_back 重载对于转换结果不可行,那么上面的代码将调用std::vector&lt;T&gt;::push_back(const T&amp;),这将导致复制。当前的规则使这个现实世界的用例更加高效。如果规则说 rvalue-refs 不能绑定到隐式转换的结果,您将不得不更改上面的代码以获得移动的效率:

v.push_back( std::string{"text"} );

恕我直言,当构造函数不明确时,必须显式构造 std::string 是没有意义的。我希望显式/隐式构造函数的行为一致,并且我希望第一个 push_back 示例更高效。

【讨论】:

  • 感谢您的链接。它描述了禁止右值引用绑定到左值的历史原因;所以读者应该注意,论文谈到的“概念”并没有进入 C++11。然而,虽然它本身很有趣,但该论文与为什么积极支持创建中间右值无关。我看不出有什么好的理由。这个问题举例说明了一个很好的反对理由(一般来说,反对的理由与普通参考文献相同:对变化的期望和类型歧视)。
  • 它没有显示反对它的理由,您问题中的代码没有任何错误,它只是没有达到您的预期。如果ugh(Bar(o) 有效,并且Bar(o) 的转换可以在所有其他上下文中隐式完成,那么为了一致性,ugh(o) 也应该有效
  • 感谢您的意见。我想也许我们在这里谈论的是交叉目的。我从可靠来源(编译器开发团队)获悉,c++ 委员会正在考虑这个特定问题;对不起,我没有 DR 号码。所以它肯定是开放的,呃,争论...... ;-)
  • 另见gcc.gnu.org/bugzilla/show_bug.cgi?id=51066(我特别指出我认为不应该对您的示例发出警告,仅在初始化局部变量时)
猜你喜欢
  • 2017-12-06
  • 2014-10-31
  • 2017-04-13
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
  • 2018-09-30
  • 1970-01-01
相关资源
最近更新 更多