ใน C ++ 11 มีวิธีการสร้างฟังก์ชัน lambda หรือไม่? หรือมันเจาะจงเกินไปที่จะรับเทมเพลต?
ฉันเข้าใจว่าฉันสามารถกำหนด class / functor คลาสสิกเทมเพลตแทน แต่คำถามเป็นเช่น: ภาษาอนุญาตให้ฟังก์ชัน lambda templating?
ใน C ++ 11 มีวิธีการสร้างฟังก์ชัน lambda หรือไม่? หรือมันเจาะจงเกินไปที่จะรับเทมเพลต?
ฉันเข้าใจว่าฉันสามารถกำหนด class / functor คลาสสิกเทมเพลตแทน แต่คำถามเป็นเช่น: ภาษาอนุญาตให้ฟังก์ชัน lambda templating?
คำตอบ:
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 กลายเป็นโจทย์ที่ง่ายอีกครั้ง อย่างไรก็ตามฉันไม่พบข้อเสนอใด ๆ :(
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
ฉันพบว่าเทคนิคนี้ช่วยเมื่อทำงานกับโค้ดเทมเพลต แต่รู้ว่ามันยังคงหมายความว่าแลมบ์ดาเองไม่สามารถเทมเพลตได้
T
จะทำงานได้ดีในdecltype(t)
ตัวอย่างนี้
ใน C ++ 11 ฟังก์ชันแลมบ์ดาไม่สามารถ templated แต่ในรุ่นถัดไปของ ISO C ++ มาตรฐาน (มักเรียกว่า C ++ 14) คุณลักษณะนี้จะถูกนำเสนอ [ที่มา]
ตัวอย่างการใช้งาน:
auto get_container_size = [] (auto container) { return container.size(); };
โปรดทราบว่าแม้ว่าไวยากรณ์จะใช้คำauto
หลักการลดประเภทจะไม่ใช้กฎของการลดauto
ประเภท แต่ใช้กฎของการลดอาร์กิวเมนต์เทมเพลตแทน ดูข้อเสนอสำหรับการแสดงออกแลมบ์ดาทั่วไป (และการอัปเดตสำหรับสิ่งนี้)
auto
หักประเภทถูกกำหนดเป็นพิเศษเช่นเดียวกับtemplate
การหักอาร์กิวเมนต์ของฟังก์ชัน
ฉันรู้ว่าคำถามนี้เกี่ยวกับ C ++ 11 อย่างไรก็ตามสำหรับผู้ที่ googled และลงสู่หน้านี้ lambdas ได้รับการสนับสนุนในตอนนี้ใน C ++ 14 และไปที่ชื่อ Generic Lambdas
[ข้อมูล] คอมไพเลอร์ยอดนิยมส่วนใหญ่สนับสนุนคุณสมบัตินี้ทันที Microsoft Visual Studio 2015 รองรับ เสียงดังกราวรองรับ GCC รองรับ
ฉันสงสัยเกี่ยวกับสิ่งนี้:
template <class something>
inline std::function<void()> templateLamda() {
return [](){ std::cout << something.memberfunc() };
}
ฉันใช้รหัสที่คล้ายกันเช่นนี้เพื่อสร้างแม่แบบและสงสัยว่าคอมไพเลอร์จะปรับฟังก์ชั่น "การตัด" ให้เหมาะสมหรือไม่
ดูที่ Boost.Phoenix สำหรับ lambdas polymorphic: http://www.boost.org/doc/libs/1_44_0/libs/spirit/phoenix/doc/html/index.html ไม่ต้องใช้ C ++ 0x โดย ทาง :)
มีนามสกุล 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>... >
ฉันได้เล่นกับเสียงดังกราวล่าสุด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;
}
นี่คือทางออกหนึ่งที่เกี่ยวข้องกับการห่อ 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
ฉันไม่แน่ใจว่าทำไมไม่มีใครแนะนำสิ่งนี้ แต่คุณสามารถเขียนฟังก์ชัน 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" (กรณีนี้ใช้เพื่อจัดทำฟังก์ชันถ่วงน้ำหนักแบบอัตโนมัติเมื่อมีคนไม่ต้องการให้น้ำหนักข้อมูลไม่ว่าข้อมูลของพวกเขาจะเป็นอะไร)