这有什么问题?
struct foo {
void process(int, char, bool) {}
};
foo myfoo;
template <typename Method> struct thing {
void doit() {
Method m = Method{};
(myfoo.*m)(5, 'a', true);
}
};
int main() {
thing<decltype(&foo::process)> t;
t.doit();
}
我认为这会隔离问题。如果我必须使用Method
类型,那么解决方法是什么,如下面原始帖子的情况那样?
原帖: 在以下尝试的测试中:
struct Foo { int play (char, bool) {return 3;} };
struct Bar { double jump (int, short, float) {return 5.8;} };
struct Baz { char run (double) {return 'b';} };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<decltype(&Foo::play), decltype(&Bar::jump), decltype(&Baz::run)> func;
func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
}
正如您所预测的那样,func
应该执行
foo.play('c', true); bar.jump(5, 2, 4.5); baz.run(6.8);
到目前为止我对Functor
类的实现(忽略完美转发等等)
template <typename... Members>
struct Functor {
using m = many_members<Members...>;
template <typename... Args>
typename m::return_types operator()(Args... args) const { // perfect forwarding to do later
auto t = std::make_tuple(args...);
auto objects = utilities::tuple_head<sizeof...(Members)>(t);
auto arguments = utilities::extract_subtuple<sizeof...(Members), sizeof...(Args) - sizeof...(Members)>(t);
call(objects, arguments); // Won't compile on GCC 7.2 or clang 6.0.
}
private:
template <typename Tuple1, typename Tuple2>
auto call (Tuple1& objects, const Tuple2& args) const {
std::invoke(typename utilities::nth_element<0, Members...>::type{}, std::get<0>(objects), 'c', true);
}
};
我使用std::invoke
的最后一行只是为了在继续之前测试这个概念。但它不会在GCC 7.2或clang 6.0上编译,所以我不能继续进行泛化。这里有任何解决方法,或完全不同的实现吗?
到目前为止,这是我所拥有的一切:
namespace utilities {
template <std::size_t N, typename... Ts>
struct nth_element : std::tuple_element<N, std::tuple<Ts...>> { };
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple&, std::enable_if_t<(Take == 0)>* = nullptr) {
return std::tuple<>();
}
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple& tuple, std::enable_if_t<(Take > 0)>* = nullptr) {
return std::tuple_cat (std::make_tuple(std::get<Skip>(tuple)), extract_subtuple<Skip + 1, Take - 1>(tuple));
}
template <std::size_t N, typename Tuple>
auto tuple_head (const Tuple& tuple) {
return extract_subtuple<0, N>(tuple);
}
}
template <typename Rs, typename Ts, typename ArgsPacks, typename AllArgs, typename... Members> struct many_members_h;
template <typename Rs, typename Ts, typename ArgsPacks, typename AllArgs>
struct many_members_h<Rs, Ts, ArgsPacks, AllArgs> {
using return_types = Rs;
using classes = Ts;
using args_packs = ArgsPacks;
using all_args = AllArgs;
};
template <typename... Rs, typename... Ts, typename... ArgsPacks, typename... AllArgs, typename R, typename T, typename... Args, typename... Rest>
struct many_members_h<std::tuple<Rs...>, std::tuple<Ts...>, std::tuple<ArgsPacks...>, std::tuple<AllArgs...>, R(T::*)(Args...), Rest...> :
many_members_h<std::tuple<Rs..., R>, std::tuple<Ts..., T>, std::tuple<ArgsPacks..., std::tuple<Args...>>, std::tuple<AllArgs..., Args...>, Rest...> { };
template <typename... Members>
struct many_members : many_members_h<std::tuple<>, std::tuple<>, std::tuple<>, std::tuple<>, Members...> { };
template <typename... Members>
struct Functor {
using m = many_members<Members...>;
template <typename... Args>
typename m::return_types operator()(Args... args) const { // perfect forwarding to do later
auto t = std::make_tuple(args...);
auto objects = utilities::tuple_head<sizeof...(Members)>(t);
auto arguments = utilities::extract_subtuple<sizeof...(Members), sizeof...(Args) - sizeof...(Members)>(t);
call(objects, arguments); // Won't compile on GCC 7.2 or clang 6.0.
}
private:
template <typename Tuple1, typename Tuple2>
auto call (Tuple1& objects, const Tuple2& args) const {
std::invoke(typename utilities::nth_element<0, Members...>::type{}, std::get<0>(objects), 'c', true);
}
};
// Testing
#include <iostream>
struct Foo { int play (char, bool) {return 3;} };
struct Bar { double jump (int, short, float) {return 5.8;} };
struct Baz { char run (double) {return 'b';} };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<decltype(&Foo::play), decltype(&Bar::jump), decltype(&Baz::run)> func;
func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
}
答案 0 :(得分:2)
采用较小的第一个示例,请注意decltype(&foo::process)
类型为void (foo::*)(int, char, bool)
。
此类型不包含或暗示与原始函数foo::process
本身的任何关联。就像int
类型不能让您在程序的其他地方获得某些特定int
的值,或类型SomeClass
不能让您参考SomeClass
对象在您的程序中的其他位置,单独的类型不具有值或身份。
表达式Method{}
value-将此指针初始化为成员类型。这意味着结果值是空指针值。这意味着调用它是未定义的行为(在许多系统上可能会导致段错误。)
如果你正在使用C ++ 17模式,你可以使用template <auto Method>
非类型参数,只需传递&foo::process
(不使用decltype
)作为模板参数。一些SFINAE技术可以强制该参数实际上是指向成员函数的指针,并且可以使用一些辅助特征来获取类类型和参数列表元组。
或者,如果您使用的是早于C ++ 17的标准,则必须使函数指针成为函数参数,或者使其成为类型后面的模板参数,如{ {1}},然后调用template <typename MethodType, MethodType Method>
。
答案 1 :(得分:0)
感谢aschepler对成员函数指针使用auto...
而不是typename...
的回答和建议,我能够实现原始目标:
#include <tuple>
#include <functional> // std::invoke
#include <type_traits>
#include <utility>
namespace utilities {
template <std::size_t N, auto I, auto... Is>
struct nth_element : nth_element<N - 1, Is...> { };
template <auto I, auto... Is>
struct nth_element<0, I, Is...> {
static constexpr decltype(I) value = I;
};
template <std::size_t N, typename Pack> struct nth_index;
template <std::size_t N, std::size_t... Is>
struct nth_index<N, std::index_sequence<Is...>> : nth_element<N, Is...> { };
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple&, std::enable_if_t<(Take == 0)>* = nullptr) {
return std::tuple<>();
}
template <std::size_t Skip, std::size_t Take, typename Tuple>
auto extract_subtuple (const Tuple& tuple, std::enable_if_t<(Take > 0)>* = nullptr) {
return std::tuple_cat (std::make_tuple(std::get<Skip>(tuple)), extract_subtuple<Skip + 1, Take - 1>(tuple));
}
template <std::size_t N, typename Tuple>
auto tuple_head (const Tuple& tuple) {
return extract_subtuple<0, N>(tuple);
}
template <typename F, typename T, typename Tuple, std::size_t... Is>
decltype(auto) invoke_with_tuple_h (F&& f, T&& t, Tuple&& tuple, std::index_sequence<Is...>&&) {
return std::invoke(std::forward<F>(f), std::forward<T>(t), std::get<Is>(std::forward<Tuple>(tuple))...);
}
template <typename F, typename T, typename Tuple>
decltype(auto) invoke_with_tuple (F&& f, T&& t, Tuple&& tuple) {
return invoke_with_tuple_h (std::forward<F>(f), std::forward<T>(t), std::forward<Tuple>(tuple), std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}
template <typename PartialSums, std::size_t Sum, std::size_t... Is> struct all_partial_sums_h;
template <std::size_t... PartialSums, std::size_t Sum>
struct all_partial_sums_h<std::index_sequence<PartialSums...>, Sum> {
using type = std::index_sequence<PartialSums..., Sum>;
using type_without_last_sum = std::index_sequence<PartialSums...>; // We define this because this is what we need actually.
};
template <std::size_t... PartialSums, std::size_t Sum, std::size_t First, std::size_t... Rest>
struct all_partial_sums_h<std::index_sequence<PartialSums...>, Sum, First, Rest...> :
all_partial_sums_h<std::index_sequence<PartialSums..., Sum>, Sum + First, Rest...> { };
template <typename Pack> struct all_partial_sums;
template <std::size_t... Is>
struct all_partial_sums<std::index_sequence<Is...>> : all_partial_sums_h<std::index_sequence<>, 0, Is...> { };
template <typename Pack> struct pack_size;
template <template <typename...> class P, typename... Ts>
struct pack_size<P<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)> { };
template <typename PackOfPacks> struct get_pack_sizes;
template <template <typename...> class P, typename... Packs>
struct get_pack_sizes<P<Packs...>> {
using type = std::index_sequence<pack_size<Packs>::value...>;
};
}
template <typename Method> struct method_traits;
template <typename R, typename C, typename... Args>
struct method_traits<R(C::*)(Args...)> {
using return_type = R;
using class_type = C;
using args_type = std::tuple<Args...>;
};
template <typename Rs, typename Cs, typename ArgsPacks, auto... Members> struct many_members_h;
template <typename Rs, typename Cs, typename ArgsPacks>
struct many_members_h<Rs, Cs, ArgsPacks> {
using return_types = Rs;
using classes = Cs;
using args_packs = ArgsPacks;
};
template <typename... Rs, typename... Cs, typename... ArgsPacks, auto F, auto... Rest>
struct many_members_h<std::tuple<Rs...>, std::tuple<Cs...>, std::tuple<ArgsPacks...>, F, Rest...> :
many_members_h<std::tuple<Rs..., typename method_traits<decltype(F)>::return_type>, std::tuple<Cs..., typename method_traits<decltype(F)>::class_type>, std::tuple<ArgsPacks..., typename method_traits<decltype(F)>::args_type>, Rest...> { };
template <auto... Members>
struct many_members : many_members_h<std::tuple<>, std::tuple<>, std::tuple<>, Members...> { };
template <auto... Members>
struct Functor {
using m = many_members<Members...>;
using starting_points = typename utilities::all_partial_sums<typename utilities::get_pack_sizes<typename m::args_packs>::type>::type;
template <typename... Args>
typename m::return_types operator()(Args&&... args) const {
constexpr std::size_t M = sizeof...(Members);
auto t = std::make_tuple(std::forward<Args>(args)...);
auto objects = utilities::tuple_head<M>(t);
auto arguments = utilities::extract_subtuple<M, sizeof...(Args) - M>(t);
return call(objects, arguments, std::make_index_sequence<M>{});
}
private:
template <typename Tuple1, typename Tuple2, std::size_t... Is>
auto call (Tuple1& objects, const Tuple2& args, std::index_sequence<Is...>&&) const { // perfect forwarding to do later
return std::make_tuple(call_helper<Is>(objects, args)...);
}
template <std::size_t N, typename Tuple1, typename Tuple2>
auto call_helper (Tuple1& objects, const Tuple2& args) const { // perfect forwarding to do later
constexpr std::size_t s = std::tuple_size_v<std::tuple_element_t<N, typename m::args_packs>>;;
constexpr std::size_t a = utilities::nth_index<N, starting_points>::value;
const auto args_tuple = utilities::extract_subtuple<a, s>(args);
return utilities::invoke_with_tuple (utilities::nth_element<N, Members...>::value, std::get<N>(objects), args_tuple);
}
};
// Testing
#include <iostream>
struct Foo { int play (char c, bool b) { std::cout << std::boolalpha << "Foo::play(" << c << ", " << b << ") called.\n"; return 3; } };
struct Bar { double jump (int a, short b, float c) { std::cout << "Bar::jump(" << a << ", " << b << ", " << c << ") called.\n"; return 5.8; } };
struct Baz { char run (double d) { std::cout << "Baz::run(" << d << ") called.\n"; return 'b'; } };
int main() {
Foo foo; Bar bar; Baz baz;
Functor<&Foo::play, &Bar::jump, &Baz::run> func;
const auto tuple = func(foo, bar, baz, 'c', true, 5, 2, 4.5, 6.8);
std::cin.get();
}
输出:
Baz::run(6.8) called.
Bar::jump(5, 2, 4.5) called.
Foo::play(c, true) called.