web-dev-qa-db-ja.com

ラムダからイニシャライザリストを返さないのはなぜですか

このコードが無効なのはなぜですか?

  auto foo=[](){
    return {1,2};     
  };

ただし、これはinitializer listは、vectorを初期化して、それ自体を返さないようにするためだけに使用されます。

auto foo=[]()->std::vector<int>{
  return {1,2};     
};

なぜ戻れないのかinitializer list?それは役に立つかもしれません。たとえば、vectorまたはlistまたは...を何かのデフォルト値で初期化するために使用できるラムダ。

25
Humam Helfawi

ラムダの戻り値の型の推定では、autoルールを使用します。これは、通常は_std::initializer_list_を適切に推定します。ただし、言語設計者は、returnステートメント([dcl.spec.auto]/7)でブレース付き初期化子リストからの推論を禁止しました。

控除がreturnステートメントに対するものであり、初期化子がbraced-init-list([dcl.init.list])の場合、プログラムの形式が正しくありません。

これは、_std::initializer_list_に参照セマンティクス( [dcl.init.list]/6 )があるためです。
[]() -> std::initializer_list<int> { return {1, 2}; }はあらゆる点で悪いです
[]() -> const int & { return 1; }。 _initializer_list_オブジェクトのバッキング配列の有効期間は、ラムダが戻ると終了し、ダングリングポインター(または2つ)が残ります。

デモ

_#include <vector>

struct Noisy {
    Noisy()  { __builtin_printf("%s\n", __PRETTY_FUNCTION__); }
    Noisy(const Noisy&) { __builtin_printf("%s\n", __PRETTY_FUNCTION__); }
    ~Noisy() { __builtin_printf("%s\n", __PRETTY_FUNCTION__); }
};

int main()
{
    auto foo = []() -> std::initializer_list<Noisy> { return {Noisy{}, Noisy{}}; };
    std::vector<Noisy> bar{foo()};
}
_

出力:

_Noisy::Noisy()
Noisy::Noisy()
Noisy::~Noisy()
Noisy::~Noisy()
Noisy::Noisy(const Noisy&)
Noisy::Noisy(const Noisy&)
Noisy::~Noisy()
Noisy::~Noisy()
_

これまでに作成されたすべてのNoisyオブジェクトが既に破棄された後、コピーコンストラクターがどのように呼び出されるかに注意してください。

26
T.C.

std::initializer_listテンプレート引数から推測することはできません。つまり、ラムダに明示的に何であるかを伝える必要があります。

#include <initializer_list>
#include <iostream>
#include <vector>

int main()
{
    auto foo = []() -> std::initializer_list<int> { return {1, 2}; };
    std::vector<int> bar{foo()};
    for (int x : bar) { std::cout << x << "  "; };
}

デモ初期化子リストの提案 からのこの背後にある理論的根拠は次のとおりです。

イニシャライザリストをテンプレート引数として使用できますか?考えてみましょう:

template<class T> void f(const T&);
f({ }); // error
f({1});
f({1,2,3,4,5,6});
f({1,2.0}); // error
f(X{1,2.0}); // ok: T is X

最後の呼び出しには明らかに問題はありません(X {1,2.0}自体が有効である場合)
テンプレート引数が[〜#〜] x [〜#〜]であるため。の任意のリストを導入していないので
types(製品タイプ)、[〜#〜] t [〜#〜]{int、doubleと推定することはできません}forf({1,2.0})、つまり呼び出しは
エラー。プレーン{}には型がないため、f({})もエラーです。

これにより、同種のリストが残ります。 f({1})およびf({1,2,3,4,5,6})を受け入れる必要があります?もしそうなら、
どういう意味ですか?もしそうなら、答えは、推定されたタイプ[〜#〜] t [〜#〜]
initializer_list。誰かがこの単純なものの少なくとも1つの良い使用法を思い付かない限り
機能(タイプ[〜#〜] e [〜#〜]の要素の同種リストは
initializer_list)、提案せず、すべての例でエラーが発生します:テンプレートなし
引数は(修飾されていない)初期化子リストから推測できます。注意する理由の1つ
これは、誰かが考えられる解釈について混乱していることを想像できるということです
単一要素リストの。たとえば、f({1})f <int>(1)を呼び出すことができますか?いいえ、それは
まったく一貫性がありません。

6
Brian Rodriguez

次のいずれかのように、関数からinitializer_listを返すことができます。

return std::initializer_list<int>{1, 2};

または

auto ret = {1, 2};
return ret;

その理由は、auto変数宣言がauto戻り値の型の推定とは異なるルールを使用しているためです。最初のものにはこの場合の特別なルールがあり、2番目のものはプレーンテンプレートタイプの推論を使用します。

これについては、Scott Meyers Effective Modern C++のアイテム2で詳しく説明されています。このトピックについては、彼から ビデオ および スライド もあります。

1
Zulan