Is returning uniform initialized reference valid?

Is this code sample valid?

using ref = char&;

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

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

seems that:

  • clang 3.5 says YES
  • gcc 4.9 says 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

    So which compiler is right? or is it unspecified?

    It very easy so overcome gcc build error by:

  • using parenthesis instead of braces

    ref foo(ref x) {
      return ref(x);
    }
    
  • by naming returned value

    ref foo(ref x) {
      ref ret{x};
      return ret;
    }
    
  • option 1. breaks uniform initialization, option 2. adds useless line of code.

    Similar question was already aked here: Why can't I initialize a reference in an initializer list with uniform initialization?

    But mentioned pr50025 is fixed in gcc 4.9.

    I know that above code sample is pretty useless, but I oversimplified it intentionally to point out the issue. In real life code problem can be hidden in a generic function like:

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

    This seems like an omission in the standard, where GCC is implementing exactly what the standard requires, and clang is going for what's probably intended.

    From C++11 (emphasis mine):

    5.2.3 Explicit type conversion (functional notation) [expr.type.conv]

    1 A simple-type-specifier (7.1.6.2) or typename-specifier (14.6) followed by a parenthesized expression-list constructs a value of the specified type given the expression list. If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4). [...]

    [...]

    3 Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (8.5.4) with the specified braced-init-list, and its value is that temporary object as a prvalue .

    For the braced-init-list case, the standard doesn't specify that that this works just like a C-style cast. And it doesn't:

    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 *
    }
    

    Unfortunately, T(expr) being equivalent to (T)expr is also the one exception in which a functional cast doesn't necessarily produce a prvalue. The standard fails to specify a similar exception for a functional cast using a braced-init-list to a reference type. As a result, in your example, ref{x} constructs a temporary of type ref , direct-list-initialised from {x} . That temporary is then treated as a prvalue, because that's what the standard says the behaviour should be, and that prvalue cannot be used for binding to an lvalue reference.

    I strongly suspect that if this were brought up to the ISO C++ committee, the standard would be changed to require clang's behaviour, but based on the current wording of the standard, I think it's GCC that's correct, at least for your specific example.

    Instead of adding a variable, or switching to parentheses, you can omit ref ( Tp ) to avoid the problem:

    template <typename Tp, typename... Us>
    Tp bar(Us&&... us) {
      return {std::forward<Us>(us)...};
    }
    
    链接地址: http://www.djcxy.com/p/24480.html

    上一篇: 从基于Spring的Java应用程序创建单个可执行JAR

    下一篇: 返回统一的初始化参考是否有效?