找到这个看起来很有趣的代码:
auto a = [](){};
class B : decltype(a)
{
};
Run Code Online (Sandbox Code Playgroud)
我想知道它的作用.这有用吗?
Whi*_*TiM 37
好吧,该代码将编译,但问题是您将无法默认构造该类1的任何对象,因为lambda的构造函数不可访问(除了复制/移动构造函数).由lambda类型保证的唯一构造函数是默认的复制/移动构造函数.并且没有默认构造函数
与lambda表达式关联的闭包类型没有默认构造函数和已删除的复制赋值运算符.它有一个默认的复制构造函数和一个默认的移动构造函数([class.copy]).[注意:这些特殊成员函数是通常隐式定义的,因此可能被定义为已删除. - 结束说明]
或者来自cppreference:
//ClosureType() = delete; //(until C++14)
ClosureType(const ClosureType& ) = default; //(since C++14)
ClosureType(ClosureType&& ) = default; //(since C++14)
Run Code Online (Sandbox Code Playgroud)
lambda构造函数无法访问的历史可以追溯到它的早期提议,在这里找到
在第3节第2段中,我引述:
在此转换中,
__some_unique_name是一个新名称,未在程序中的其他位置使用,其方式会导致与其用作闭包类型的冲突.此名称和类的构造函数不需要向用户公开 - 用户在闭包类型中可以依赖的唯一功能是复制构造函数(如果该提议被批准,则为移动构造函数)和函数调用运算符.闭包类型不需要默认构造函数,赋值运算符或函数调用之外的任何其他访问方式.对于可实现性而言,禁止从闭包类型创建派生类可能是值得的....
如您所见,该提案甚至建议禁止从闭包类型创建派生类.
1 当然,您可以复制初始化基类,a以初始化类型的对象B.看到这个
现在,问你的问题:
这有用吗?
不是你的确切形式.您的只能通过实例进行实例化a.但是,如果从一个普通的Callable类继承,例如lambda类型,我可以想到两种情况.
创建一个Functor,在给定的继承序列中调用一组仿函数:
一个简化的例子:
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...);
}
Run Code Online (Sandbox Code Playgroud)
示例用法:
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;
}
Run Code Online (Sandbox Code Playgroud)
产量
Before TrimAndCapitalize: str = " what a Hullabaloo "
After TrimAndCapitalize: str = "WHAT A HULLABALOO"
Run Code Online (Sandbox Code Playgroud)
在科利鲁看到它
创建一个带有重载的Functor operator()(...),重载所有基类' operator()(...):
另一个很酷的技巧:因为结果类型make_functionSequence(...)是一个可调用的类.您可以在以后附加更多lambda或callable.
//.... 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);
Run Code Online (Sandbox Code Playgroud)
Lambdas是下面的功能对象,带有额外的合成糖.a评估类似的东西(MyLambda名称是随机名称,就像你做的那样namespace {}- 命名空间名称将是随机的):
class MyLambda {
public:
void operator()() {
}
}
Run Code Online (Sandbox Code Playgroud)
因此,当您从lambda继承时,您正在做的是从匿名类/结构继承.
至于有用性,它和任何其他继承一样有用.您可以在一个对象中具有多个具有多重继承的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;
}
Run Code Online (Sandbox Code Playgroud)
您问题中的代码段不会在任何地方以精确的形式显示.但是你可以看到lambda的类型被推断出来了apply_visitor_inline.然后实例化一个继承自所有这些lambdas的类.目的?出于诸如此类的目的,我们能够将多个lambda组合成一个lambdas apply_visitor.此函数期望接收单个函数对象,该对象定义多个operator()并基于重载区分它们.但有时候定义一个对我们必须涵盖的每种类型进行操作的lambda更方便.在这种情况下,lambdas的继承提供了一种组合机制.
我从这里得到了内联访问者的想法:https://github.com/exclipy/inline_variant_visitor,虽然我没有看那里的实现,所以这个实现是我自己的(但我猜它非常相似).
编辑:由于clang中的错误,最初发布的代码才起作用.根据这个问题(C++中的重载lambdas以及clang和gcc之间的差异),operator()基类中的多个查找是不明确的,实际上我最初发布的代码没有在gcc中编译.新代码在两者中编译并且应该是兼容的.可悲的是,似乎没有办法做到用语句可变参数,因此必须使用递归.