ラムダ関数をテンプレート化できますか?


230

C ++ 11では、ラムダ関数をテンプレート化する方法はありますか?それともテンプレート化するには本質的に具体的すぎますか?

代わりに、古典的なテンプレートクラス/ファンクタを定義できることを理解していますが、問題はより似ています:言語はテンプレートのラムダ関数を許可しますか?


ラムダテンプレートが役立つユースケースはありますか?
James McNellis、2010

7
ジェームズ:タプルを反復処理する関数を作成できます(必ずしも有用ではありません)。
Joe D

Stroustrupのインタビューを読みながら、メタテンプレートの複雑さが問題であることを話しているときに、そのアイデアを思いつきました。許可されていれば、この機能の組み合わせで遊んでいる

回答:


181

UPDATE 2018:C ++ 20には、テンプレート化および概念化されたラムダが付属します。この機能はすでに標準ドラフトに統合されています。


UPDATE 2014:C ++ 14は今年リリースされ、現在、この例と同じ構文の多形ラムダを提供しています。一部の主要なコンパイラはすでにそれを実装しています。


現状では(C ++ 11では)残念ながら違います。ポリモーフィックラムダは、柔軟性とパワーの点で優れています。

彼らが単相性になった最初の理由は、概念のためでした。概念により、このコードの状況は困難になりました。

template <Constraint T>
void foo(T x)
{
    auto bar = [](auto x){}; // imaginary syntax
}

制約付きテンプレートでは、他の制約付きテンプレートのみを呼び出すことができます。(それ以外の場合、制約はチェックできませんでした。)foo呼び出すことができますbar(x)か?ラムダにはどのような制約がありますか(結局のところ、そのパラメーターは単なるテンプレートです)?

コンセプトはこの種のものに取り組む準備ができていませんでした。late_check(概念が呼び出されるまでチェックされなかった場合)などのようなものがさらに必要になります。単純にそれをすべて落とし、単相ラムダに固執するだけでした。

ただし、C ++ 0xから概念が削除されたため、多形ラムダは再び単純な命題になります。しかし、私はそれについての提案を見つけることができません。:(


5
シンプル...コンセプトを再導入し、それらを複雑にする機能を避けたいという欲求があることを除いて。

6
コンセプトよりも多相ラムダの方がいいと思います。この例がどのように動機付けられるかはわかりません。単純にエラーとして禁止し、ラムダを単相性[](T x){}または制約付きテンプレート[] template <Constraint T>(T x){}にする必要があります。これらは静的に一致することを確認できます。これが不可能な理由はありますか?
DrPizza 2011

13
概念と多相ラムダのどちらかを選択する必要はありません。cpp
Dave Abrahams

3
多形ラムダの提案は次のとおりです。open
Radif Sharafullin

18
ポリモーフィックラムダはC ++ 14に含まれる予定です。少なくともコミュニティドラフトには含まれます:)
Arne Mertz 2013年

37

C ++ 11ラムダは他の回答で述べられているようにテンプレート化できませんがdecltype()、テンプレート化されたクラスまたは関数内でラムダを使用するときに役立つようです。

#include <iostream>
#include <string>

using namespace std;

template<typename T>
void boring_template_fn(T t){
    auto identity = [](decltype(t) t){ return t;};
    std::cout << identity(t) << std::endl;
}

int main(int argc, char *argv[]) {
    std::string s("My string");
    boring_template_fn(s);
    boring_template_fn(1024);
    boring_template_fn(true);
}

プリント:

My string
1024
1

この方法はテンプレート化されたコードを扱うときに役立つことがわかりましたが、それでもラムダ自体をテンプレート化できないことを認識しています。


26
Tdecltype(t)この例ではの代わりにうまく機能します。
user2023370 2015年

26

C ++ 11ではラムダ関数をテンプレート化できませんが、ISO C ++標準の次のバージョン(C ++ 14と呼ばれることが多い)では、この機能が導入されます。[ソース]

使用例:

auto get_container_size = [] (auto container) { return container.size(); };

構文ではキーワードをauto使用しますが、auto型の推定では型の推定の規則は使用されず、代わりにテンプレート引数の推定の規則が使用されます。一般的なラムダ式の提案(およびこれに対する更新)も参照してください。


5
auto型の推論の規則は、template関数の引数の推論の規則と同じになるように明確に定義されています。
underscore_d

10

この質問はC ++ 11に関するものであることは承知しています。ただし、グーグルしてこのページにアクセスした人のために、テンプレート化されたラムダはC ++ 14でサポートされ、Generic Lambdasという名前で使用されます。

[info]現在人気のあるコンパイラのほとんどがこの機能をサポートしています。Microsoft Visual Studio 2015がサポートします。Clangがサポートします。GCCがサポートします。


6

これはどうですか?

template <class something>
inline std::function<void()> templateLamda() {
  return [](){ std::cout << something.memberfunc() };
}

このようなコードを使用してテンプレートを生成し、コンパイラーが「ラッピング」関数を最適化するかどうか疑問に思いました。


2
どのコンパイラー?それをやった?
NicoBerrogorry

4

多形ラムダのBoost.Phoenixをご覧ください。http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html 必要ありません仕方 :)


2
私はすでにそれについて知っていますが、問題はとにかく新しい標準についてです;)
Klaim

わかりました:) C ++ 0xラムダは単相であり、残念ながらテンプレート化できません。
usta

3

ラムダテンプレートを許可するgcc拡張があります。

// create the widgets and set the label
base::for_each(_widgets, [] <typename Key_T, typename Widget_T>
                         (boost::fusion::pair<Key_T, Widget_T*>& pair) -> void {
                             pair.second = new Widget_T();
                             pair.second->set_label_str(Key_T::label);
                          }
              );

どこ_widgetsstd::tuple< fusion::pair<Key_T, Widget_T>... >


FWIW、これはC ++ 20の標準構文になりました。
LF

2

私はフラグversion 5.0.1を使用してコンパイルしている最新のclangで遊んでおり-std=c++17、ラムダの自動型パラメーターのいくつかの素晴らしいサポートがあります:

#include <iostream>
#include <vector>
#include <stdexcept>

int main() {
    auto slice = [](auto input, int beg, int end) {
        using T = decltype(input);
        const auto size = input.size();
        if (beg > size || end > size || beg < 0 || end < 0) {
            throw std::out_of_range("beg/end must be between [0, input.size())");
        }
        if (beg > end) {
            throw std::invalid_argument("beg must be less than end");
        }
        return T(input.begin() + beg, input.begin() + end);
    };
    auto v = std::vector<int> { 1,2,3,4,5 };
    for (auto e : slice(v, 1, 4)) {
        std::cout << e << " ";
    }
    std::cout << std::endl;
}

1

以下は、ランバを構造体でラップすることを含む1つのソリューションです。

template <typename T>                                                   
struct LamT                                                             
{                                                                       
   static void Go()                                                     
   {                                                                    
      auto lam = []()                                                   
      {                                                                 
         T var;                                                         
         std::cout << "lam, type = " << typeid(var).name() << std::endl;
      };                                                                

      lam();                                                            
   }                                                                    
};   

doを使用するには:

LamT<int>::Go();  
LamT<char>::Go(); 
#This prints 
lam, type = i
lam, type = c

これの主な問題(追加のタイピング以外に)は、この構造定義を別のメソッド内に埋め込むことができないか、または(gcc 4.9)を取得します。

error: a template declaration cannot appear at block scope

私もこれをやってみました:

template <typename T> using LamdaT = decltype(                          
   [](void)                                                          
   {                                                                 
       std::cout << "LambT type = " << typeid(T).name() << std::endl;  
   });

このように使用できることを願って:

LamdaT<int>();      
LamdaT<char>();

しかし、コンパイラエラーが発生します。

error: lambda-expression in unevaluated context

したがって、これは機能しません...しかし、コンパイルできたとしても、「テンプレートを使用しているため」「目的のLamdaT」をファイルスコープに配置する必要があるため、用途が限られます。ラムダ。


1

なぜだれもこれを提案していない理由はわかりませんが、ラムダ関数を返すテンプレート関数を作成できます。以下は私の問題、私がこのページに来た理由を解決しました:

template <typename DATUM>
std::function<double(DATUM)> makeUnweighted() {
  return [](DATUM datum){return 1.0;};
}

与えられたタイプの引数(例えばstd::string)を取る関数が欲しいときはいつでも、

auto f = makeUnweighted<std::string>()

そして今f("any string")戻る1.0

これが、「テンプレート化されたラムダ関数」の意味する例です。(この特定のケースは、データが何であれ、誰かがデータに重み付けしたくない場合に、自動的に不活性な重み付け関数を提供するために使用されます。)


2
これは、ラムダを作成する前にラムダの引数の型がわかっている場合にのみ機能します。その場合、特定の型のラムダを引数として使用できます。ポリモーフィックラムダのポイントは、作業コードを記述したときにわからない引数型に対して実行する作業を提供することです。基本的に、これはまったく異なります。そのため、提案されていません。
Klaim

ああ、そうです。私はそのユースケースについては考えていませんでした-ラムダ関数はオンザフライのものと考え、そのような多態性は多目的ライブラリの何かと考えています。私は、任意のタイプのユーザーのラムダ関数を受け入れ、適切なタイプのデフォルトを提供する必要があるテンプレートライブラリを作成していました。
ジムピバルスキ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.