我正在尝试编写一个泛型函数,它连接两个可以用同一组参数调用的函数,但是我遇到了一些麻烦。这是我到目前为止所拥有的。 (不编译)
//A functor to store the input functions and call them
template <typename LEFT, typename RIGHT>
struct combine_functions {
combine_functions(const LEFT &left, const RIGHT &right)
: left(left), right(right) {}
template <typename ...ARGS>
std::enable_if_t<
//My compiler doesn't have support for C++17 std library so I
//found an implementation of callable on SO
is_callable_v<LEFT, std::decay_t<ARGS>...> &&
is_callable_v<RIGHT, std::decay_t<ARGS>...>
> operator()(ARGS... args) const {
//the return value doesn't matter in my situation and can be
//completely discarded
left(std::forward<ARGS>(args)...);
right(std::forward<ARGS>(args)...);
}
private:
mutable LEFT left;
mutable RIGHT right;
};
//I should probably have an enable if that checks the arguments
//are function pointers or functors
template <typename LEFT, typename RIGHT>
combine_functions<
std::decay_t<LEFT>,
std::decay_t<RIGHT>
>
operator+(
const LEFT &left,
const RIGHT &right
) {
return {left, right};
}
如果不清楚我想要实现的目标,那么这是一个测试。
#include <iostream>
#include "combine functions.hpp"
struct A {
void operator()(float &f, int i) {
std::cout << "running A with float " << f << " and int " << i << '\n';
f++;
}
};
struct B {
void operator()(float &f, int i) {
std::cout << "running B with float " << f << " and int " << i << '\n';
f++;
}
};
struct C {
void operator()(float &f, int i) {
std::cout << "running C with float " << f << " and int " << i << '\n';
f++;
}
};
int main(int, const char**) {
A a;
B b;
C c;
auto abc = concat(concat(a, b), c);
//or
//auto abc = a + b + c;
std::function<void(float &, int)> abcFunc = abc;
float f = 5.0f;
int i = 9;
abcFunc(f, i);
return EXIT_SUCCESS;
}
这是预期的输出
running A with float 5 and int 9
running B with float 6 and int 9
running C with float 7 and int 9
提前感谢您的帮助。
答案 0 :(得分:3)
我认为这是一个合理的起点。支持任意数量的连接和任意数量的具有完美转发的参数:
#include <tuple>
#include <utility>
#include <iostream>
namespace detail
{
template<class Tuple, std::size_t...Is, class...Args>
void exec(Tuple&& tuple, std::index_sequence<Is...>, Args&&...args)
{
using expand = int[];
void(expand{
0,
(std::get<Is>(tuple)(std::forward<Args>(args)...),0)...
});
}
}
template<class...Funcs>
auto concat(Funcs&&...funcs)
{
constexpr auto nof_funcs = sizeof...(funcs);
return [funcs = std::make_tuple(std::forward<Funcs>(funcs)...)](auto&&...args) mutable
{
detail::exec(funcs,
std::make_index_sequence<nof_funcs>(),
std::forward<decltype(args)>(args)...);
};
};
int main()
{
auto f1 = [](auto&& arg) { std::cout << arg << std::endl; };
auto f2 = [](auto&& arg) { std::cerr << arg << std::endl; };
concat(f1, f2)("Hello, World");
}
答案 1 :(得分:2)
您可以使用以下内容:
template <typename LEFT, typename RIGHT>
struct combine_functions {
private:
LEFT left;
RIGHT right;
public:
combine_functions(const LEFT& left, const RIGHT& right)
: left(left), right(right) {}
template <typename ...ARGS>
auto operator()(ARGS&... args) const
-> decltype(left(args...), static_cast<void>(right(args...)))
{
left(args...);
right(args...);
}
};
template <typename LEFT, typename RIGHT>
combine_functions<std::decay_t<LEFT>, std::decay_t<RIGHT>>
concat(const LEFT& left, const RIGHT& right)
{
return {left, right};
}
我不使用过于通用且匹配太多类型的operator +
。
我不使用std::forward
,因为您不希望move
(right
会调用移动的对象...)
答案 2 :(得分:1)
如何在C ++中实现它?
我通常不会只为某人编写代码,但这很简单。
#include <iostream>
#include <functional>
#include <string>
using namespace std;
template <typename Left, typename Right>
class ConcatFn {
public:
ConcatFn(Left left, Right right)
: left(left)
, right(right) {
}
template <typename... Args>
void operator()(Args... args) {
this->left(args...);
this->right(args...);
}
private:
function<Left> left;
function<Right> right;
};
void A(const char *foo) {
cout << "A: " << foo << endl;
}
void B(string bar) {
cout << "B: " << bar << endl;
}
int main() {
ConcatFn<void(const char *), void(string)> fn(A, B);
fn("hello!");
return 0;
}
输出:
$ ./concat
A: hello!
B: hello!
我认为你不会放弃上面fn
声明中的模板参数。
此外,如果您想保证函数具有确切的签名,只需删除第二个模板参数(Right
)并使用Left
(理想情况下重命名)到处Right
是使用
在这种情况下使用重载运算符是不明智的吗?
完全没有。
“连接”是此操作的最佳术语吗?
可能不是,但看看这个用例是多么罕见,我不知道任何“标准”术语。函数链接或分组,也许?
答案 3 :(得分:0)
看起来正确,只是在这里和那里有几个怪癖
#include<type_traits>
#include<utility>
template<typename L, typename R>
struct combined
{
typename std::decay<L>::type l;
typename std::decay<R>::type r;
combined(L&& l, R&& r)
: l(std::forward<L>(l)), r(std::forward<R>(r)) {}
template<typename... Args>
void operator()(Args&&... args)
{
l(args...);
r(std::forward<Args>(args)...);
}
};
template<typename L, typename R>
combined<L, R> combine(L&& l, R&& r)
{
return {std::forward<L>(l), std::forward<R>(r)};
}
首先,最可能必须存储可调用对象,因此std::remove_reference
。
std::enable_if
ais是不必要的,因为当对象被错误地调用时,编译器会发出错误。
std::forward
被称为perfect forwarding,非常重要。
EDIT3 经过辩论,第一个forward
被取消以防止意外动作发生,感谢Rerito,R。Martinho Fernandes和Jarod42
operator+
感觉非常奇怪,因为您没有添加返回的值,这与函数添加的定义冲突。