从lambdainheritance是什么意思?

发现这个代码看起来很有趣:

auto a = [](){}; class B : decltype(a) { }; 

我想知道它做了什么。 这可以用任何方式吗?

那么这个代码将会被编译,但问题在于你将无法默认构造该类的任何对象,因为lambda构造函数不可访问(复制/移动构造函数除外)lambdatypes保证的唯一构造函数是一个默认的复制/移动构造函数。 并没有默认的构造函数

[expr.prim.lambda / 21]

与lambdaexpression式关联的闭包types没有默认的构造函数和删除的拷贝赋值操作符。 它有一个默认的拷贝构造函数和一个默认的移动构造函数([class.copy])。 [注意:这些特殊的成员函数是像往常一样隐式定义的,因此可能被定义为删除。 – 结束注意]

或从cppreference :

 //ClosureType() = delete; //(until C++14) ClosureType(const ClosureType& ) = default; //(since C++14) ClosureType(ClosureType&& ) = default; //(since C++14) 

无法访问的lambda构造函数的历史可以追溯到早期的提议,在这里find

在第三节第二段,我引用:

在这个翻译中, __some_unique_name是一个新的名字,不会在程序中的其他地方以一种与封闭types的使用相冲突的方式使用。 这个名称和类的构造函数不需要公开给用户,用户在闭包types中唯一可以依赖的特性是复制构造函数(如果该提议被批准,则为移动构造函数)函数调用操作符。 闭包types不需要默认的构造函数,赋值运算符或除函数调用之外的其他任何访问方式。 可执行性禁止从闭包types创build派生类可能是值得的。 …

正如你所看到的,这个build议甚至build议应该禁止从闭包types创build派生类。


1当然,你可以复制 – 用a初始化基类,以初始化typesB的对象。 看到这个


现在,对你的问题:

这可以用任何方式吗?

不是在你的确切forms。 你的将只能用实例a来实例化。 但是,如果您inheritance了一个通用的Callable类(如lambdatypes),则可以考虑两种情况。

  1. 创build一个Functor,在给定的inheritance序列中调用一组函子:

    一个简单的例子:

     template<typename TFirst, typename... TRemaining> class FunctionSequence : public TFirst, FunctionSequence<TRemaining...> { public: FunctionSequence(TFirst first, TRemaining... remaining) : TFirst(first), FunctionSequence<TRemaining...>(remaining...) {} template<typename... Args> decltype(auto) operator () (Args&&... args){ return FunctionSequence<TRemaining...>::operator() ( TFirst::operator()(std::forward<Arg>(args)...) ); } }; template<typename T> class FunctionSequence<T> : public T { public: FunctionSequence(T t) : T(t) {} using T::operator(); }; template<typename... T> auto make_functionSequence(T... t){ return FunctionSequence<T...>(t...); } 

    示例用法:

     int main(){ //note: these lambda functions are bug ridden. Its just for simplicity here. //For correct version, see the one on coliru, read on. auto trimLeft = [](std::string& str) -> std::string& { str.erase(0, str.find_first_not_of(' ')); return str; }; auto trimRight = [](std::string& str) -> std::string& { str.erase(str.find_last_not_of(' ')+1); return str; }; auto capitalize = [](std::string& str) -> std::string& { for(auto& x : str) x = std::toupper(x); return str; }; auto trimAndCapitalize = make_functionSequence(trimLeft, trimRight, capitalize); std::string str = " what a Hullabaloo "; std::cout << "Before TrimAndCapitalize: str = \"" << str << "\"\n"; trimAndCapitalize(str); std::cout << "After TrimAndCapitalize: str = \"" << str << "\"\n"; return 0; } 

    产量

     Before TrimAndCapitalize: str = " what a Hullabaloo " After TrimAndCapitalize: str = "WHAT A HULLABALOO" 

    看到它住在Coliru

  2. 用重载的operator()(...)创build一个Functor,并重载所有基类的operator()(...)

    • 尼尔·弗里德曼在回答这个问题时已经给出了一个很好的例子。
    • 我也起草了一个类似的,简化的例子,从他的。 在Coliru上看到它
    • Jason Lucas还在他的CppCon 2014展示“与工会的多态性”中展示了它的实际应用。 你可以在这里findRepo 这个源代码的确切位置之一(谢谢Cameron DaCamara)

另一个很酷的技巧:由于make_functionSequence(...)的结果types是一个可调用的类。 您可以在稍后添加更多的lambda或可调用的。

  //.... As previously seen auto trimAndCapitalize = make_functionSequence(trimLeft, trimRight, capitalize); auto replace = [](std::string& str) -> std::string& { str.replace(0, 4, "Whaaaaat"); return str; }; //Add more Functors/lambdas to the original trimAndCapitalize auto replaced = make_functionSequence(trimAndCapitalize, replace /*, ... */); replaced(str2); 

Lambdas是下面的function对象 ,还有其他的合成糖。 a评估类似的东西(与MyLambda名字是一个随机的名字,就像当你做namespace {} – 命名空间的名称将是随机的):

 class MyLambda { public: void operator()() { } } 

所以当你从一个lambdainheritance,你正在做的是从一个匿名类/结构inheritance。

至于有用性,那么它和其他inheritance一样有用。 你可以在一个对象中拥有多重inheritance的多重lambda函数,你可以添加新的方法来扩展它。 我现在想不出任何真正的应用,但我相信有很多。

请参阅此问题以获取更多信息。

这实际上可能是相当有用的,但是这取决于你想要怎样指导整个事情。 考虑下面的代码:

 #include <boost/variant.hpp> #include <iostream> #include <string> #include <unordered_map> template <class R, class T, class ... Ts> struct Inheritor : public T, Inheritor<R, Ts...> { using T::operator(); using Inheritor<R, Ts...>::operator(); Inheritor(T t, Ts ... ts) : T(t), Inheritor<R, Ts...>(ts...) {} }; template <class R, class T> struct Inheritor<R, T> : public boost::static_visitor<R>, T { using T::operator(); Inheritor(T t) : T(t) {} }; template <class R, class V, class ... T> auto apply_visitor_inline(V& v, T ... t) { Inheritor<R, T...> i(t...); return boost::apply_visitor(i, v); } int main() { boost::variant< int, std::string > u("hello world"); boost::variant< int, std::string > u2(5); auto result = apply_visitor_inline<int64_t>(u, [] (int i) { return i;}, [] (const std::string& s) { return s.size();}); auto result2 = apply_visitor_inline<int64_t>(u2, [] (int i) { return i;}, [] (const std::string& s) { return s.size();}); std::cout << result; std::cout << result2; } 

您问题中的代码段不会以确切的forms显示在任何地方。 但是你可以看到lambda的types在apply_visitor_inline中被推断出来。 一个类然后被实例化,从所有这些lambdainheritance。 目的? 我们能够将多个lambda组合成一个单一的lambda,像apply_visitor这样的目的。 这个函数希望接收一个定义多个operator()函数对象,并根据重载区分它们。 但是有时候定义一个lambda函数会更方便一些,这个lambda函数对我们必须覆盖的每个types都有效。 在这种情况下,lambda的inheritance提供了一种组合的机制。

我从这里得到了内联访问者的想法: https : //github.com/exclipy/inline_variant_visitor ,虽然我没有看到那里的实现,所以这个实现是我自己的(但我猜它非常相似)。

编辑:最初发布的代码只能工作,因为在铛错误。 根据这个问题( 在C ++中重载lambdas以及clang和gcc之间的区别 ),在基类中查找多个operator()是不明确的,事实上我发布的代码最初并没有在gcc中编译。 新代码在两者中编译并应符合规定。 可悲的是,似乎没有办法使用可变语句,所以必须使用recursion。