ジェネリックラムダはC ++ 14でどのように機能しますか?


114

autoC ++ 14標準でジェネリックラムダはどのように機能しますか(引数の型としてのキーワード)?

それは、C ++テンプレートに基づいていますか?異なる引数型ごとに、コンパイラーは同じ本体を使用して新しい関数を生成しますが、型を置き換えます(コンパイル時のポリモーフィズム)、それともJavaのジェネリック(型消去)に似ていますか?

コード例:

auto glambda = [](auto a) { return a; };

6
問題のC ++ 11を最初に使用したC ++ 14に修正
sasha.sochka 2013

回答:


130

ジェネリックラムダはで導入されましたC++14

簡単に言うと、ラムダ式で定義されたクロージャタイプには、のラムダの通常の非テンプレート呼び出し演算子ではなく、テンプレート化された呼び出し演算子がありC++11ます(もちろん、autoパラメータリストに少なくとも1回出現する場合)。

だからあなたの例:

auto glambda = [] (auto a) { return a; };

glambdaこのタイプのインスタンスを作成します:

class /* unnamed */
{
public:
    template<typename T>
    T operator () (T a) const { return a; }
};

C ++ 14標準ドラフトn3690の5.1.2 / 5項では、特定のラムダ式のクロージャタイプの呼び出し演算子を定義する方法を指定しています。

非ジェネリックなラムダ式のクロージャタイプには、パブリックインライン関数呼び出し演算子(13.5.4)があり、そのパラメータと戻り値のタイプは、ラムダ式のパラメータ宣言節とトレーリングリターンタイプによってそれぞれ記述されます。ジェネリックラムダの場合、クロージャタイプにはパブリックインライン関数呼び出し演算子メンバーテンプレート(14.5.2)があり、そのtemplate-parameter-listは、ラムダのparameter-declaration-clauseでautoが出現するたびに1つの発明された型template-parameterで構成されます。出現順に。発明された型template-parameterは、対応するパラメーター宣言が関数パラメーターパック(8.3.5)を宣言する場合のパラメーターパックです。関数呼び出し演算子テンプレートの戻り値の型と関数パラメーターは、ラムダ式の後続戻り型とパラメーター宣言句から、parameter-declaration-clauseのdecl-specifiers内の各autoを、対応する発明されたテンプレートパラメータ。

最後に:

さまざまな引数の型ごとに、コンパイラーは同じ本体で型が変更された関数を生成するテンプレートに似ていますか、それともJavaのジェネリックに似ていますか?

上記の段落で説明したように、ジェネリックラムダは、テンプレート化された呼び出し演算子を持つ一意で名前のないファンクターの構文糖衣にすぎません。それはあなたの質問に答えるはずです:)


7
ただし、テンプレートメソッドを使用してローカルに定義されたクラスも許可されます。どちらが新しいですか。
Yakk-Adam Nevraumont 2013年

2
@Yakk:関数ローカルテンプレートの制限は、すでにC ++ 11で完全に削除されていませんか?
セバスチャンマッハ

2
@phresnel:いいえ、その制限は解除されていません
Andy Prowl

1
@AndyProwl:私は間違いを認識しています。実際に解除されたのは、ローカル型をテンプレート引数として使用することでした(のようにint main () { struct X {}; std::vector<X> x; }
Sebastian Mach

1
@phresnel:そうです、確かに変わりました
Andy Prowl

25

残念ながら、それらはC ++ 11の一部ではありません(http://ideone.com/NsqYuq):

auto glambda = [](auto a) { return a; };

int main() {}

g ++ 4.7の場合:

prog.cpp:1:24: error: parameter declared auto
...

ただしポートランドの一般的なラムダの提案に従って、C ++ 14で実装する方法は次のとおりです。

[](const& x, & y){ return x + y; }

これにより、ほとんどの場合、通常は匿名ファンクタークラスが作成されますが、型がないため、コンパイラーはテンプレート化されたメンバーを生成しoperator()ます。

struct anonymous
{
    template <typename T, typename U>
    auto operator()(T const& x, U& y) const -> decltype(x+y)
    { return x + y; }
};

または、新しい(一般的な(多形)ラムダ式の提案)の提案に従って

auto L = [](const auto& x, auto& y){ return x + y; };

--->

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;

したがって、はい、パラメータのすべての順列に対して、新しいインスタンス化が発生しますが、そのファンクタのメンバーは共有されます(つまり、キャプチャされた引数)。


6
型指定子を削除できるようにするという提案は、まったくグロテスクです。
オービットのライトネスレース

彼らはg ++-4.9を採用しました。あなたが供給する必要があり-std=c++1yます。
emsr 2014年

ideoneにはまだgcc-4.9とC ++ 14がないことに気づきませんでした。
emsr 2014年

質問:これautoは従来の自動車と同じ控除規則を持っていますか?テンプレート化されたアナロジーに言及する場合、それは自動が自動ではないことを意味します。これはテンプレートタイプの控除と同じルールです。次に問題は:テンプレートの控除はと同等autoですか?
v.oddou

@ v.oddou:「クラシックオート」がいい。私には、「古典的なオート」と「スタック変数」、そしてかつてとは対照的に使用されたstaticか、register使用して、はい、とにかく:) autoボンネットの下に、通常のテンプレートが生成されていることが意味を。実際、ラムダはコンパイラ内部でファンクタクラスに置き換えられ、autoパラメータはそれtemplate <T> ... (T ...)が発行されることを意味します。
セバスチャンマッハ

17

これは、C ++ 14に提案された機能(C ++ 11にはない)であり、テンプレートに似ています(または同等でさえあります)。たとえば、N3559は次の例を提供します。

たとえば、次の一般的なラムダ式を含むステートメント:

auto L = [](const auto& x, auto& y){ return x + y; };

その結果、クロージャタイプが作成され、オブジェクトは以下の構造体と同様に動作します。

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.