使用 lambdas 进行变量访问的最佳方法
我想使用 lambda 内联对变体类型的访问.目前我有以下代码:
I want to inline visitation of variant types with lambdas. At the moment i have the following code:
struct Foo {
boost::variant< boost::blank , int , string , vector< int > > var;
template <typename T, typename IL , typename SL , typename VL>
void ApplyOptionals( T& ref, IL&& intOption , SL&& stringOption , VL&& vectorOption ) {
if (var.which() == 1) {
intOption( ref , boost::get< int >(var) );
} else if (var.which() ==2) {
stringOption( ref , boost::get< string>(var) );
} else if (var.which() == 3) {
vectorOption( ref , boost::get< vector< int > >(var) );
}
};
};
// ...
myFooV.ApplyOptionals(
obj,
[](Obj& o, int v) -> void { cout << "int: " << v << endl; o.value = v; },
[](Obj& o, string v) -> void{ cout << "string: " << v << endl; o.name = v; },
[](Obj& o, vector< int >& v) -> void{ v.push_back(257); cout << " vector.. has elements: " << v.size() << endl; o.ids = v; }
);
然而,这种方法的主要缺点是它取决于变体类型参数的顺序,并且不会在编译时检测像 boost::static_visitor
这样的未处理类型
However the main drawback of this approach is that it depends on the order of variant type parameters and doesn't detect at compile-time unhandled types like boost::static_visitor
would do
我可以从这两种方法中获得最佳效果吗?
Can i get the best of both approaches?
正在研究 RMartinho 的出色答案,我正在尝试解决此错误,似乎该变体认为 operator() 调用不明确(我使用的是 g++ 4.5.1,就像它看不到一样lambda 运算符.
Working on the excellent answer from RMartinho, i'm trying to work out this error, it seems that variant thinks the operator() calls are amibiguous (i'm using g++ 4.5.1, it's like it couldn't see the lambda operators.
看这个问题请求成员`...'是模棱两可的g++,似乎c++不喜欢用多重继承作为提供多重重载的方式(即使由于签名不同,调用完全没有歧义)
looking at this question request for member `...' is ambiguous in g++, it seems that c++ doesn't like multiple inheritance as a way to provide multiple overloads (even if the calls are completely non-ambiguous because of different signature)
#include <iostream>
#include <string>
#include <vector>
#include <boost/variant.hpp>
using namespace std;
typedef boost::variant< boost::blank , int , string , vector< int > > var_t;
template <typename ReturnType, typename... Lambdas>
struct lambda_visitor : public boost::static_visitor<ReturnType>, public Lambdas... {
lambda_visitor(Lambdas... lambdas) : Lambdas(lambdas)... { }
};
template <typename ReturnType>
struct lambda_visitor<ReturnType> : public boost::static_visitor<ReturnType> {
lambda_visitor() {}
};
template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, Lambdas...> make_lambda_visitor(Lambdas... lambdas) {
return { lambdas... };
// you can use the following instead if your compiler doesn't
// support list-initialization yet
// return lambda_visitor<ReturnType, Lambdas...>(lambdas...);
}
int main() {
vector< int > vit;
vit.push_back(7);
var_t myFooV = vit;
auto visitor = make_lambda_visitor<void>(
[](int v) -> void { cout << "int: " << v << endl; },
[](string& v) -> void{ cout << "string: " << v << endl; },
[](vector< int >& v) -> void{ v.push_back(27); boost::get< vector< int > >(myFooV).push_back(34); cout << " vector.. has elements: " << v.size() << endl; }
);
cout << " and for the grand finale.. " << endl;
boost::apply_visitor( visitor , myFooV );
};
这给了我大致一堆模板提升错误,但不同的部分是:
This, gives me roughly a bunch of template boost errors, but the distinct part is:
boost_1_46_0/boost/variant/variant.hpp:832:32: error: request for member ‘operator()’ is ambiguous
test2.cpp:44:54: error: candidates are: main()::<lambda(std::vector<int>&)>
test2.cpp:43:47: error: main()::<lambda(std::string&)>
test2.cpp:42:55: error: main()::<lambda(int)>
boost_1_46_0/boost/variant/variant.hpp:832:32: error: return-statement with a value, in function returning 'void'
这是整个错误,以防万一我遗漏了一些其他相关信息:
This is the whole error, just in case i'm missing some other relevant info:
boost_1_46_0/boost/variant/variant.hpp: In member function ‘boost::detail::variant::invoke_visitor<Visitor>::result_type boost::detail::variant::invoke_visitor<Visitor>::internal_visit(T&, int) [with T = std::vector<int>, Visitor = lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> >, boost::detail::variant::invoke_visitor<Visitor>::result_type = void]’:
boost_1_46_0/boost/variant/detail/visitation_impl.hpp:130:9: instantiated from ‘typename Visitor::result_type boost::detail::variant::visitation_impl_invoke_impl(int, Visitor&, VoidPtrCV, T*, mpl_::true_) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, T = std::vector<int>, typename Visitor::result_type = void, mpl_::true_ = mpl_::bool_<true>]’
boost_1_46_0/boost/variant/detail/visitation_impl.hpp:173:9: instantiated from ‘typename Visitor::result_type boost::detail::variant::visitation_impl_invoke(int, Visitor&, VoidPtrCV, T*, NoBackupFlag, int) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, T = std::vector<int>, NoBackupFlag = boost::variant<boost::blank, int, std::basic_string<char>, std::vector<int> >::has_fallback_type_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/detail/visitation_impl.hpp:260:1: instantiated from ‘typename Visitor::result_type boost::detail::variant::visitation_impl(int, int, Visitor&, VoidPtrCV, mpl_::false_, NoBackupFlag, Which*, step0*) [with Which = mpl_::int_<0>, step0 = boost::detail::variant::visitation_impl_step<boost::mpl::l_iter<boost::mpl::l_item<mpl_::long_<4l>, boost::blank, boost::mpl::l_item<mpl_::long_<3l>, int, boost::mpl::l_item<mpl_::long_<2l>, std::basic_string<char>, boost::mpl::l_item<mpl_::long_<1l>, std::vector<int>, boost::mpl::l_end> > > > >, boost::mpl::l_iter<boost::mpl::l_end> >, Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, NoBackupFlag = boost::variant<boost::blank, int, std::basic_string<char>, std::vector<int> >::has_fallback_type_, typename Visitor::result_type = void, mpl_::false_ = mpl_::bool_<false>]’
boost_1_46_0/boost/variant/variant.hpp:1776:13: instantiated from ‘static typename Visitor::result_type boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::internal_apply_visitor_impl(int, int, Visitor&, VoidPtrCV) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, VoidPtrCV = void*, T0_ = boost::blank, T1 = int, T2 = std::basic_string<char>, T3 = std::vector<int>, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/variant.hpp:1787:13: instantiated from ‘typename Visitor::result_type boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::internal_apply_visitor(Visitor&) [with Visitor = boost::detail::variant::invoke_visitor<lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> > >, T0_ = boost::blank, T1 = int, T2 = std::basic_string<char>, T3 = std::vector<int>, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/variant.hpp:1810:52: instantiated from ‘typename Visitor::result_type boost::variant<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>::apply_visitor(Visitor&) [with Visitor = lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> >, T0_ = boost::blank, T1 = int, T2 = std::basic_string<char>, T3 = std::vector<int>, T4 = boost::detail::variant::void_, T5 = boost::detail::variant::void_, T6 = boost::detail::variant::void_, T7 = boost::detail::variant::void_, T8 = boost::detail::variant::void_, T9 = boost::detail::variant::void_, T10 = boost::detail::variant::void_, T11 = boost::detail::variant::void_, T12 = boost::detail::variant::void_, T13 = boost::detail::variant::void_, T14 = boost::detail::variant::void_, T15 = boost::detail::variant::void_, T16 = boost::detail::variant::void_, T17 = boost::detail::variant::void_, T18 = boost::detail::variant::void_, T19 = boost::detail::variant::void_, typename Visitor::result_type = void]’
boost_1_46_0/boost/variant/detail/apply_visitor_unary.hpp:60:43: instantiated from ‘typename Visitor::result_type boost::apply_visitor(Visitor&, Visitable&) [with Visitor = lambda_visitor<void, main()::<lambda(int)>, main()::<lambda(std::string&)>, main()::<lambda(std::vector<int>&)> >, Visitable = boost::variant<boost::blank, int, std::basic_string<char>, std::vector<int> >, typename Visitor::result_type = void]’
test2.cpp:49:40: instantiated from here
boost_1_46_0/boost/variant/variant.hpp:832:32: error: request for member ‘operator()’ is ambiguous
test2.cpp:44:54: error: candidates are: main()::<lambda(std::vector<int>&)>
test2.cpp:43:47: error: main()::<lambda(std::string&)>
test2.cpp:42:55: error: main()::<lambda(int)>
boost_1_46_0/boost/variant/variant.hpp:832:32: error: return-statement with a value, in function returning 'void'
结论:
我想添加此实用程序的最终版本,包括测试:
conclusion:
I want to add the final version of this utility, including tests:
#include <boost/variant.hpp>
namespace Variant {
template <typename ReturnType, typename... Lambdas>
struct lambda_visitor;
template <typename ReturnType, typename Lambda1, typename... Lambdas>
struct lambda_visitor< ReturnType, Lambda1, Lambdas...>
: public lambda_visitor<ReturnType, Lambdas...>, public Lambda1 {
using Lambda1::operator();
using lambda_visitor< ReturnType, Lambdas...>::operator();
typedef ReturnType ReturnType_t;
lambda_visitor(Lambda1 l1, Lambdas... lambdas) : Lambda1(l1), lambda_visitor< ReturnType, Lambdas...> (lambdas...) {
}
lambda_visitor(Lambda1 && l1, Lambdas && ... lambdas) : Lambda1(l1), lambda_visitor< ReturnType, Lambdas...> (lambdas...) {
}
};
template <typename ReturnType, typename Lambda1>
struct lambda_visitor<ReturnType, Lambda1>
: public boost::static_visitor<ReturnType>, public Lambda1 {
using Lambda1::operator();
typedef ReturnType ReturnType_t;
lambda_visitor(Lambda1 l1) : boost::static_visitor<ReturnType > (), Lambda1(l1) {
}
lambda_visitor(Lambda1 && l1) : boost::static_visitor<ReturnType > (), Lambda1(l1) {
}
};
template <typename ReturnType>
struct lambda_visitor<ReturnType> : public boost::static_visitor<ReturnType> {
typedef ReturnType ReturnType_t;
lambda_visitor() : boost::static_visitor<ReturnType > () {
}
};
template <typename ReturnType>
struct default_blank_visitor {
typedef ReturnType ReturnType_t;
inline ReturnType operator() (const boost::blank&) const {
return (ReturnType) 0;
};
};
template<>
struct default_blank_visitor<void> {
typedef void ReturnType_t;
inline void operator() (const boost::blank&) const {};
};
template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, default_blank_visitor< ReturnType >, Lambdas...> make_lambda_visitor(Lambdas... lambdas) {
return
{
default_blank_visitor<ReturnType > (), lambdas...
};
// you can use the following instead if your compiler doesn't
// support list-initialization yet
//return lambda_visitor<ReturnType, default_blank_visitor<ReturnType> , Lambdas...>( default_blank_visitor<ReturnType>(), lambdas...);
};
/*
template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, default_blank_visitor< ReturnType >, Lambdas...> make_lambda_visitor(Lambdas && ... lambdas) {
return
{
default_blank_visitor<ReturnType > (), lambdas...
};
// you can use the following instead if your compiler doesn't
// support list-initialization yet
//return lambda_visitor<ReturnType, default_blank_visitor<ReturnType> , Lambdas...>( default_blank_visitor<ReturnType>(), lambdas...);
};*/
template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, Lambdas...> make_lambda_visitor_override_blank(Lambdas... lambdas) {
return
{
lambdas...
};
// you can use the following instead if your compiler doesn't
// support list-initialization yet
//return lambda_visitor<ReturnType, Lambdas...>(lambdas...);
}
namespace basic_usage
{
struct Test
{
typedef boost::variant< boost::blank , int , double > variant_t;
void run()
{
variant_t a, b, c;
a = 42;
b = 3.14159265;
auto visitor = Variant::make_lambda_visitor<int>( [](int v) -> int { return v+1; } , [](double v) -> int { return (int)v*2; } );
int result = boost::apply_visitor(visitor, a);
HAssertMsg( result == (42 + 1) , "unexpected");
result = boost::apply_visitor( visitor , b);
HAssertMsg( result == 6 , "unexpected");
auto blankVisitor = Variant::make_lambda_visitor_override_blank<int>(
[](int v) -> int { return -1; }
, [](double v) -> int { return -1; }
, [](boost::blank ) -> int { return 0;} );
result = boost::apply_visitor( blankVisitor , c);
HAssertMsg( result == 0 , "unexpected");
//same as previous case, but using lambda coalescing :-)
auto blankVisitor2 = Variant::make_lambda_visitor_override_blank<int>(
[](boost::variant< int , double >& v) -> int { return -1; }
, [](boost::blank ) -> int { return 0;} );
result = boost::apply_visitor( blankVisitor2 , c);
HAssertMsg( result == 0 , "unexpected");
result = boost::apply_visitor( blankVisitor2 , a);
HAssertMsg( result == -1 , "unexpected");
result = boost::apply_visitor( blankVisitor2 , b);
HAssertMsg( result == -1 , "unexpected");
}
};
}
};
推荐答案
您可以使用可变参数模板来获取 lambda,并使用继承构建一个变体访问者.这将保留编译时检查.
You could use variadic templates to take the lambdas, and build a variant visitor using inheritance. That would retain the compile time checks.
template <typename ReturnType, typename... Lambdas>
struct lambda_visitor : public static_visitor<ReturnType>, public Lambdas... {
lambda_visitor(Lambdas... lambdas) : Lambdas(lambdas)... {}
};
还有一个小辅助函数来使用参数类型推导(lambdas 需要):
And a little helper function to use argument type deduction (required for lambdas):
template <typename ReturnType, typename... Lambdas>
lambda_visitor<ReturnType, Lambdas...> make_lambda_visitor(Lambdas... lambdas) {
return { lambdas... };
// you can use the following instead if your compiler doesn't
// support list-initialization yet
// return lambda_visitor<ReturnType, Lambdas...>(lambdas...);
}
现在您可以让访问者像这样:
Now you can make visitors like this:
auto visitor = make_lambda_visitor<int>([](int) { return 42; },
[](std::string) { return 17; },
[](std::vector<int>) { return 23; });
注意:由于我不知道重载解析过程的细节,这个优雅的解决方案会导致奇怪的歧义错误:(
Note: due to a detail of the overload resolution process that I wasn't aware of, this elegant solution causes weird ambiguity errors :(
请参阅后续问题以了解修复方法.
See the follow-up question for the fix.
相关文章