返回统一初始化参考有效吗?

时间:2015-01-28 17:59:40

标签: c++11 gcc reference clang uniform-initialization

此代码示例是否有效?

using ref = char&;

ref foo(ref x) {
  return ref{x};
}

int main() {
  char a;
  foo(a);
  return 0;
}

似乎:

  • clang 3.5说YES
  • gcc 4.9说NO

    main.cpp: In function 'char& foo(ref)':
    main.cpp:4:15: error: invalid cast of an rvalue expression of type 'char' to type 'ref {aka char&}'
       return ref{x};
                   ^
    

http://coliru.stacked-crooked.com/a/cb6604b81083393f

那么哪个编译器是对的?还是未指明?

通过以下方式克服gcc构建错误非常容易:

  1. 使用括号代替大括号

    ref foo(ref x) {
      return ref(x);
    }
    
  2. 命名返回值

    ref foo(ref x) {
      ref ret{x};
      return ret;
    }
    
  3. 选项1.打破统一初始化,选项2.添加无用的代码行。

    这里已经有类似的问题了: Why can't I initialize a reference in an initializer list with uniform initialization?

    但提到pr50025已在gcc 4.9中修复。

    我知道上面的代码示例相当无用, 但我故意过分简化它以指出问题。 在现实生活中,代码问题可以隐藏在通用函数中,如:

    #include <utility>
    template <typename Tp, typename... Us>
    Tp bar(Us&&... us) {
      return Tp{std::forward<Us>(us)...};
    }
    

1 个答案:

答案 0 :(得分:3)

这似乎是标准中的遗漏,GCC正在严格执行标准所要求的内容,并且clang正在寻找可能的目标。

来自C ++ 11(强调我的):

  

5.2.3显式类型转换(功能表示法)[expr.type.conv]

     

1 简单类型说明符(7.1.6.2)或 typename-specifier (14.6),后跟带括号的表达式列表给定表达式列表构造指定类型的值。如果表达式列表是单个表达式,则类型转换表达式与相应的强制转换表达式(5.4)等效(在定义中,如果在含义中定义)。 [...]

     

[...]

     

3同样,简单类型说明符 typename-specifier 后跟 braced-init-list 会创建一个临时对象指定的类型direct-list-initialized(8.5.4)与指定的 braced-init-list ,其值是临时对象作为prvalue

对于 braced-init-list 的情况,标准并没有指定它的工作方式就像C风格的转换。它没有:

typedef char *cp;
int main() {
  int i;
  (cp(&i)); // okay: C-style casts can be used as reinterpret_cast
  (cp{&i}); // error: no implicit conversion from int * to char *
}

不幸的是,T(expr)等同于(T)expr也是一个例外,其中功能强制转换不会必然会产生prvalue。标准未能为使用braced-init-list到引用类型的功能强制转换指定类似的异常。因此,在您的示例中,ref{x}构造了一个ref类型的临时文件,从{x}开始直接列表初始化。然后将该临时值视为prvalue,因为该标准表示该行为应该是什么,并且prvalue不能用于绑定到左值引用。

我强烈怀疑,如果将其提交给ISO C ++委员会,标准将被更改为要求clang的行为,但根据标准的当前措辞,我认为它是GCC这是正确的,至少对你的具体例子而言。

您可以省略refTp)来避免此问题,而不是添加变量或切换到括号:

template <typename Tp, typename... Us>
Tp bar(Us&&... us) {
  return {std::forward<Us>(us)...};
}