Mas*_*ler 2 c++ sfinae variadic-templates c++17
我的同事给我做了一个“小测验”,他让他的学生解决了一次。似乎我虚弱的头脑无法理解现代C ++功能的所有美妙之处。
主题:
实现一个join函数,接受任意函子并返回与它们中的任何一个一样的函子。例如:
{
auto result = std::visit(custom::join(
[](std::string const& s) { return "it's a string"; },
[](std::pair<int, int> const& p) { return "it's a pair"; }
), var);
assert(result == "it's a string");
var = std::make_pair(10, 20);
auto lvalue_lambda = [](std::string const& s) { return "it's a string"; };
result = std::visit(custom::join(
lvalue_lambda,
[](std::pair<int, int> const& p) { return "it's a pair"; }
), var);
assert(result == "it's a pair");
}
Run Code Online (Sandbox Code Playgroud)
好吧,经过一番思考,我明白了这个想法,std::variant就“类型安全的联合”而言,这意味着“列出的一个”,因此我需要一个元组。尝试过这样的事情:
namespace custom
{
template<typename ...Functors>
class ResultFunctor
{
public:
ResultFunctor(Functors&&... funcs)
: m_funcs(std::make_tuple(std::move(funcs)...))
{}
template<typename ...Params>
auto operator()(Params... params) // that's where I got stuck
{
// return std::get<void(Params...)>(m_funcs)(params...); // No, the return type spoils this idea
return std::get<0>(m_funcs)(params...); // Now I need to choose the correct functor
}
private:
std::tuple<Functors...> m_funcs;
};
template<typename ...Functors>
ResultFunctor<Functors...> join(Functors&&... funcs)
{
return ResultFunctor(std::move(funcs)...);
}
}
Run Code Online (Sandbox Code Playgroud)
如果仅用于具有void返回类型的函子,则可以轻松获得所需的元组元素。但是似乎无法确定它,不能从给定参数推导返回类型(显然)。
另一个想法是使用一些SFINAE技巧来选择正确的operator()()版本,但是通过这种方式或另一种方式,我将不得不“遍历”所有元组项(这很讨厌,但仍然可以通过谷歌搜索),然后检查该项是否为适合的,基于给定的参数包。
好吧,那是我停下来思考一下的地方。如果有人(谁能更好地处理各种杂文)有任何想法,我将不胜感激。
这是一个非常简单的解决方案,不涉及SFINAE或模板元编程(仅常规模板)。
第一步是编写一个代表关节过载组的函子。这很容易通过继承来实现,并且因为用作输入的所有函子必须具有不同的类型,所以我们不必做任何花哨的事情。
// This represents overload set
template<class F1, class F2>
struct Joint : public F1, public F2 {
using F1::operator();
using F2::operator();
};
Run Code Online (Sandbox Code Playgroud)
为了方便用户,我们可以添加一个推导指南:
template<class F1, class F2>
Joint(F1, F2) -> Joint<F1, F2>;
Run Code Online (Sandbox Code Playgroud)
因为Joint是C ++ 17及更高版本中的聚合类型,所以我们不必提供构造函数,因为我们可以使用聚合初始化:
// This code magically works
auto result = std::visit(Joint{
[](std::string const& s) { return "it's a string"; },
[](std::pair<int, int> const& p) { return "it's a pair"; }
}, var);
Run Code Online (Sandbox Code Playgroud)
编写custom::join函数同样简单:
template<class F1, class F2>
auto join(F1&& f1, F2&& f2) {
return Joint { std::forward<F1>(f1), std::forward<F2>(f2) };
}
Run Code Online (Sandbox Code Playgroud)
现在有了基本案例,我们可以很容易地将其概括:
template<class F, class F2, class... Fs>
auto join(F&& f, F2&& f2, Fs&&... fs) {
return Joint{
std::forward<F>(f),
join(std::forward<F2>(f2), std::forward<Fs>(fs)...)
};
}
Run Code Online (Sandbox Code Playgroud)