web-dev-qa-db-ja.com

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

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

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

207
Klaim

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


更新2014:C++ 14が今年リリースされ、現在この例と同じ構文の多態性ラムダを提供しています。いくつかの主要なコンパイラは既にそれを実装しています。


現状では(C++ 11で)、残念ながらいいえです。多態性ラムダは、柔軟性とパワーの点で優れています。

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

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

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

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

ただし、C++ 0xから概念が削除されたことで、ポリモーフィックラムダは再び単純な命題になりました。しかし、私はそれに関する提案を見つけることができません。 :(

164
GManNickG

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

この手法は、テンプレート化されたコードで作業する場合に役立ちますが、ラムダ自体をテンプレート化できないことを意味していることに気付きました。

36
Joel

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

使用例:

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

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

23

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

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

9
Ram

これはどうだろうか:

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

このようなコードを使用して、テンプレートを生成し、コンパイラが「ラッピング」機能を最適化するかどうかを考えました。

6
ted

多相ラムダについてはBoost.Phoenixをご覧ください: http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html 不要C++ 0x、ところで:)

4
usta

gcc拡張機能があり、lambdaテンプレートを許可します。

// 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>... >です

3
user6559931

私は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
Doug Coburn

構造体にLambaをラップする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
rmccabe3701

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

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を返します。

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

1
Jim Pivarski