找到这个看起来很有趣的代码:
component.options.props = {
size: Number,
type: {
type: Number,
required: false,
twoWay: false
}
我想知道它的作用。这有用吗?
答案 0 :(得分:35)
那么,代码会编译,但问题是你将无法默认构造该类 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)
lambda构造函数无法访问的历史记录可追溯到其早期提案,here
在第3节第2段中,我引用:
在此翻译中,
__some_unique_name
是一个新名称,未使用 程序中的其他地方可能会导致与其冲突 用作封闭类型。这个名称,以及该类的构造函数, 不需要向用户公开 - 用户的唯一功能 可以依赖于闭包类型的是一个复制构造函数(和一个移动 构造函数(如果该提议被批准)和函数调用 运营商。闭包类型不需要默认构造函数,赋值 运算符,或超出函数调用的任何其他访问方式。有可能 值得实现以禁止创建派生类 从封闭类型。 ...
如您所见,该提案甚至建议禁止从闭包类型创建派生类。
1 当然,您可以使用a
复制初始化基类,以初始化类型为B
的对象。见this
现在,问题:
这可以用于任何方式吗?
不是您的确切形式。您只能使用实例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...);
}
示例用法:
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"
创建一个带有重载operator()(...)
的Functor,重载了所有基类“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);
答案 1 :(得分:8)
Lambdas下面是function objects,还有额外的合成糖。 a
评估为类似的名称(MyLambda
名称是随机名称,就像您创建namespace {}
时一样 - 命名空间名称将是随机的):
class MyLambda {
public:
void operator()() {
}
}
因此,当您从lambda继承时,您正在做的是从匿名类/结构继承。
至于有用性,它和任何其他继承一样有用。您可以在一个对象中具有多个具有多重继承的lambdas的功能,您可以向其添加新方法以扩展它。我现在无法想到任何真正的应用,但我确定有很多应用。
有关详细信息,请参阅this question。
答案 2 :(得分:6)
这实际上非常有用,但这取决于你对整个事情的直接了解。请考虑以下代码:
#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;
}
您问题中的代码段在任何地方都没有以精确的形式显示。但是你可以看到在apply_visitor_inline
中推断出lambda的类型。然后实例化一个继承自所有这些lambdas的类。目的?我们能够将多个lambda组合成一个lambdas,用于apply_visitor
之类的目的。此函数期望接收定义多个operator()
的单个函数对象,并根据重载区分它们。但有时候定义一个对我们必须涵盖的每种类型进行操作的lambda更方便。在这种情况下,lambdas的继承提供了一种组合机制。
我从这里得到了内联访问者的想法:https://github.com/exclipy/inline_variant_visitor,虽然我没有看到那里的实现,所以这个实现是我自己的(但我猜它非常相似)。
编辑:由于clang中的错误,最初发布的代码才有效。根据这个问题(Overloaded lambdas in C++ and differences between clang and gcc),基类中多个operator()
的查找是不明确的,实际上我发布的代码实际上并没有在gcc中编译。新代码在两者中编译并且应该是兼容的。遗憾的是,似乎没有办法使用可变参数使用语句,因此必须使用递归。