ฟังก์ชั่นแลมบ์ดาสามารถ Templated ได้หรือไม่?


230

ใน C ++ 11 มีวิธีการสร้างฟังก์ชัน lambda หรือไม่? หรือมันเจาะจงเกินไปที่จะรับเทมเพลต?

ฉันเข้าใจว่าฉันสามารถกำหนด class / functor คลาสสิกเทมเพลตแทน แต่คำถามเป็นเช่น: ภาษาอนุญาตให้ฟังก์ชัน lambda templating?


มีกรณีการใช้งานที่แม่แบบแลมบ์ดาจะมีประโยชน์หรือไม่?
James McNellis

7
James: คุณสามารถสร้างฟังก์ชั่นเพื่อทำซ้ำทับสิ่งอันดับ (ไม่จำเป็นต้องมีประโยชน์)
Joe D

ฉันคิดถึงความคิดในขณะที่อ่านบทสัมภาษณ์ของ Stroustrup ซึ่งพูดถึงความซับซ้อนของเมตา - เทมเพลตซึ่งเป็นปัญหา หากได้รับอนุญาตผมก็จินตนาการนินจารหัสฟูที่อาจจะมีการประดิษฐ์คิดค้นโดยโปรแกรมเมอร์ที่ฉลาดเกินไปเล่นกับเรื่องนี้มีการรวมกัน ...
Klaim

คำตอบ:


181

UPDATE 2018: C ++ 20 จะมาพร้อมกับ lambdas ที่สร้างเทมเพลตและแนวคิด สถานที่นี้ได้รับการรวมเข้ากับร่างมาตรฐานแล้ว


UPDATE 2014: C ++ 14 เปิดตัวในปีนี้และตอนนี้ให้ lambdas Polymorphic ด้วยไวยากรณ์เดียวกับในตัวอย่างนี้ คอมไพเลอร์รายใหญ่บางรายได้ใช้งานแล้ว


มันอยู่ที่ (ใน C ++ 11) ไม่น่าเศร้า Polymorphic lambdas นั้นยอดเยี่ยมในด้านความยืดหยุ่นและพลัง

เหตุผลดั้งเดิมที่พวกเขากลายเป็น monomorphic นั้นเป็นเพราะแนวคิด แนวคิดทำให้สถานการณ์โค้ดนี้ยาก:

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

ในเทมเพลตที่มีข้อ จำกัด คุณสามารถโทรได้เฉพาะเทมเพลตที่มีข้อ จำกัด อื่น ๆ (ไม่สามารถตรวจสอบข้อ จำกัด ได้) สามารถfooเรียกใช้bar(x)หรือไม่ แลมบ์ดามีข้อ จำกัด อะไรบ้าง (พารามิเตอร์ของมันเป็นเพียงแม่แบบหลังจากทั้งหมด)?

แนวคิดไม่พร้อมที่จะแก้ไขปัญหานี้ มันต้องการสิ่งต่าง ๆ เพิ่มเติมเช่นlate_check(ซึ่งไม่ได้ตรวจสอบแนวคิดจนกว่าจะเรียกใช้) และสิ่งต่างๆ เรียบง่ายขึ้นเพียงแค่วางมันทั้งหมดและติดกับ lambdas monomorphic

อย่างไรก็ตามด้วยการลบแนวคิดจาก C ++ 0x แลมบ์ดา polymorphic กลายเป็นโจทย์ที่ง่ายอีกครั้ง อย่างไรก็ตามฉันไม่พบข้อเสนอใด ๆ :(


5
ง่าย ... ยกเว้นมีความต้องการที่จะรื้อฟื้นแนวคิดใหม่และหลีกเลี่ยงคุณสมบัติที่ทำให้ซับซ้อน

6
ฉันคิดว่าฉันควรจะมีลูกแกะโพลีมอร์ฟิคมากกว่าแนวความคิด ฉันไม่เข้าใจว่าตัวอย่างกระตุ้นสิ่งใด คุณสามารถห้ามมันเป็นข้อผิดพลาดได้และต้องการแลมบ์ดาให้เป็น monomorphic [] (T x) {} หรือเทมเพลตที่ จำกัด [] เท็มเพลต <ข้อ จำกัด T> (T x) {} ซึ่งสามารถยืนยันแบบคงที่ให้ตรงกัน มีเหตุผลบางอย่างว่าทำไมถึงเป็นไปไม่ได้?
DrPizza

13
คุณไม่ต้องเลือกระหว่างแนวความคิดและ lambdas แบบ polymorphic: cpp-next.com/archive/2011/12/a-breakthrough-for-concepts
Dave Abrahams

3
นี่คือข้อเสนอสำหรับ polymorphic lambdas: open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdfและการใช้ของเล่นใน clang: faisalv.github.com/clang-glambda
Radif Sharafullin

18
Polymorphic Lambdas จะอยู่ใน C ++ 14 อย่างน้อยพวกเขาก็อยู่ใน Community Draft ภายในตอนนี้ :)
Arne Mertz

37

C ++ 11 lambdas ไม่สามารถ templated ตามที่ระบุไว้ในคำตอบอื่น ๆ แต่decltype()ดูเหมือนว่าจะช่วยได้เมื่อใช้ lambda ภายในคลาสหรือฟังก์ชันที่ templated

#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
Tจะทำงานได้ดีในdecltype(t)ตัวอย่างนี้
user2023370

26

ใน C ++ 11 ฟังก์ชันแลมบ์ดาไม่สามารถ templated แต่ในรุ่นถัดไปของ ISO C ++ มาตรฐาน (มักเรียกว่า C ++ 14) คุณลักษณะนี้จะถูกนำเสนอ [ที่มา]

ตัวอย่างการใช้งาน:

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

โปรดทราบว่าแม้ว่าไวยากรณ์จะใช้คำautoหลักการลดประเภทจะไม่ใช้กฎของการลดautoประเภท แต่ใช้กฎของการลดอาร์กิวเมนต์เทมเพลตแทน ดูข้อเสนอสำหรับการแสดงออกแลมบ์ดาทั่วไป (และการอัปเดตสำหรับสิ่งนี้)


5
กฎการautoหักประเภทถูกกำหนดเป็นพิเศษเช่นเดียวกับtemplateการหักอาร์กิวเมนต์ของฟังก์ชัน
underscore_d

10

ฉันรู้ว่าคำถามนี้เกี่ยวกับ C ++ 11 อย่างไรก็ตามสำหรับผู้ที่ googled และลงสู่หน้านี้ lambdas ได้รับการสนับสนุนในตอนนี้ใน C ++ 14 และไปที่ชื่อ Generic Lambdas

[ข้อมูล] คอมไพเลอร์ยอดนิยมส่วนใหญ่สนับสนุนคุณสมบัตินี้ทันที Microsoft Visual Studio 2015 รองรับ เสียงดังกราวรองรับ GCC รองรับ


6

ฉันสงสัยเกี่ยวกับสิ่งนี้:

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

ฉันใช้รหัสที่คล้ายกันเช่นนี้เพื่อสร้างแม่แบบและสงสัยว่าคอมไพเลอร์จะปรับฟังก์ชั่น "การตัด" ให้เหมาะสมหรือไม่


2
คอมไพเลอร์อะไร? ใช่ไหม
NicoBerrogorry

4

ดูที่ Boost.Phoenix สำหรับ lambdas polymorphic: http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html ไม่ต้องใช้ C ++ 0x โดย ทาง :)


2
ฉันรู้แล้ว แต่คำถามนั้นเกี่ยวกับมาตรฐานใหม่อยู่แล้ว;)
Klaim

ตกลง :) C ++ 0x lambdas นั้น monomorphic และไม่สามารถ templated โชคไม่ดี
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);
                          }
              );

ที่_widgetsเป็นstd::tuple< fusion::pair<Key_T, Widget_T>... >


FWIW นี่เป็นไวยากรณ์มาตรฐานใน C ++ 20
LF

2

ฉันได้เล่นกับเสียงดังกราวล่าสุดversion 5.0.1รวบรวมกับ-std=c++17ธงและตอนนี้มีการสนับสนุนที่ดีสำหรับพารามิเตอร์ประเภทอัตโนมัติสำหรับ lambdas:

#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

นี่คือทางออกหนึ่งที่เกี่ยวข้องกับการห่อ lamba ในโครงสร้าง:

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

      lam();                                                            
   }                                                                    
};   

วิธีใช้:

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" ที่ขอบเขตไฟล์ (เพราะมันเป็นเทมเพลต) ซึ่งเอาชนะวัตถุประสงค์ของ lambdas


1

ฉันไม่แน่ใจว่าทำไมไม่มีใครแนะนำสิ่งนี้ แต่คุณสามารถเขียนฟังก์ชัน templated ที่ส่งกลับฟังก์ชัน lambda ต่อไปนี้แก้ไขปัญหาของฉันเหตุผลที่ฉันมาที่หน้านี้:

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มา

นั่นคือตัวอย่างของสิ่งที่ฉันหมายถึงโดย "ฟังก์ชั่นแลมบ์ดา templated" (กรณีนี้ใช้เพื่อจัดทำฟังก์ชันถ่วงน้ำหนักแบบอัตโนมัติเมื่อมีคนไม่ต้องการให้น้ำหนักข้อมูลไม่ว่าข้อมูลของพวกเขาจะเป็นอะไร)


2
วิธีนี้ใช้ได้เฉพาะถ้าคุณทราบชนิดของการโต้แย้งสำหรับแลมบ์ดาก่อนสร้างแลมบ์ดาซึ่งในกรณีนี้คุณสามารถใช้แลมบ์ดาที่มีประเภทเฉพาะเป็นอาร์กิวเมนต์ จุดของแลมบ์ดา polymorphic คือการจัดเตรียมงานที่ต้องทำในประเภทอาร์กิวเมนต์ที่คุณไม่เคยรู้เมื่อคุณเขียนรหัสงาน โดยทั่วไปสิ่งนี้แตกต่างกันโดยสิ้นเชิงซึ่งเป็นสาเหตุที่ไม่แนะนำ
Klaim

อ่าเข้าใจแล้ว ฉันไม่คิดว่ากรณีการใช้งาน --- ฉันคิดว่าฟังก์ชั่นแลมบ์ดาเป็นสิ่งที่กำลังดำเนินอยู่และความหลากหลายที่เป็นสิ่งต่าง ๆ ในห้องสมุดอเนกประสงค์ ฉันกำลังเขียนไลบรารี templated ที่ต้องยอมรับฟังก์ชั่นแลมบ์ดาของผู้ใช้ทุกประเภทและยังให้ค่าเริ่มต้นของประเภทที่เหมาะสม
จิม Pivarski
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.