7

このコード サンプルは有効ですか?

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

オプション 1. 一様な初期化を中断し、オプション 2. 無駄なコード行を追加します。

同様の質問がすでにここで聞かれました: なぜ一様な初期化でイニシャライザ リストの参照を初期化できないのですか?

ただし、前述の pr50025 は gcc 4.9 で修正されています。

上記のコード サンプルが役に立たないことはわかっていますが、問題を指摘するために意図的に単純化しすぎています。実際のコードの問題は、次のような一般的な関数に隠されている可能性があります。

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

1 に答える 1

3

これは、GCCが標準が必要とするものを正確に実装しており、clangがおそらく意図されていることを行っている標準の省略のようです。

C++11 から (強調鉱山):

5.2.3 明示的な型変換(関数表記)【expr.type.conv】

1 simple-type-specifier (7.1.6.2) またはtypename-specifier (14.6) の後に括弧で囲まれた式リストが続くと、指定された式リストの値が構築されます。式リストが単一の式である場合、型変換式は対応するキャスト式 (5.4) と (定義されている場合も、意味が定義されている場合も) 同等です。[...]

[...]

3 同様に、 braced -init-listが後に続くsimple-type-specifierまたはtypename-specifierは、指定された braced -init-listを使用して、指定された直接リスト初期化型 (8.5.4) の一時オブジェクトを作成し、そのvalue は、 prvalue としてのその一時オブジェクトです

ブレース初期化リストの場合、標準では、これが 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 を生成しない唯一の例外でもあります。標準は、波括弧初期化リストを参照型に使用する関数キャストに対して同様の例外を指定していません。その結果、あなたの例では、直接リスト初期化されたref{x}タイプの一時を構築します。その一時的な値は prvalue として扱われます。これは、標準では動作がそうあるべきであり、その prvalue を左辺値参照へのバインドに使用できないためです。ref{x}

これがISO C ++委員会に持ち込まれた場合、clangの動作を要求するように標準が変更されると強く思いますが、標準の現在の文言に基づいて、少なくともあなたの特定の例ではGCCが正しいと思います。

変数を追加したり、括弧に切り替えたりする代わりに、ref( Tp) を省略して問題を回避できます。

template <typename Tp, typename... Us>
Tp bar(Us&&... us) {
  return {std::forward<Us>(us)...};
}
于 2015-01-28T20:27:16.517 に答える