从lambdainheritance是什么意思?
发现这个代码看起来很有趣:
auto a = [](){}; class B : decltype(a) { };
我想知道它做了什么。 这可以用任何方式吗?
那么这个代码将会被编译,但问题在于你将无法默认构造该类的任何对象,因为lambda的构造函数不可访问(复制/移动构造函数除外) 。 lambda
types保证的唯一构造函数是一个默认的复制/移动构造函数。 并没有默认的构造函数
[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),则可以考虑两种情况。
-
创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
-
用重载的
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。