gpt4 book ai didi

c++ - 如何使用来自不同继承层次级别的多个虚函数指针作为模板参数?

转载 作者:行者123 更新时间:2023-11-30 01:41:54 26 4
gpt4 key购买 nike

我在使用虚函数指针作为模板参数时遇到问题。问题似乎是编译器不会查找基类中的所有函数,或者无法将它们视为派生类的函数。

struct B1
{
virtual void b1() = 0;
virtual ~B1() = default;
};

struct B2
{
virtual void b2() = 0;
virtual ~B2() = default;
};

struct D1
: virtual public B1
{
void b1() override {}
};

struct D12
: virtual public D1
, virtual public B2
{
void b2() override {}
};

帮助类为给定实例执行一系列成员函数。

template<
typename T,
void(T::*...Fs)()>
struct Executor
{
static
void
execute(
T & t)
{}
};

template<
typename T,
void(T::*F)(),
void(T::*...Fs)()>
struct Executor<
T, F, Fs...>
{
static
void
execute(
T & t)
{
(t.*F)();
Executor<T, Fs...>::execute(t);
}
};

按给定顺序灵活执行函数的实际类

template<
typename T,
void(T::*...Fs)()>
struct FlexBind
{
std::unique_ptr<T> t;
void b()
{
Executor<T, Fs...>::execute(*t);
}
};

我的用例是我喜欢静态定义函数的调用顺序(编译时),但调用这些函数的对象实例是动态定义的(运行时)。

int main()
{
FlexBind<D12, D12::b1, D12::b2> FB1;//compile error
FlexBind<D12, D12::b2, D12::b1> FB2;
FB1.t.reset(new D12());
FB1.b();
FB2.t.reset(new D12());
FB2.b();
return 0;
}

我得到的错误是:

error: '&D1::b1' is not a valid template argument for type
'void (D12::*)()' because it is of type 'void (D1::*)()'

编译器无法匹配void (D12::*)()void (D1::*)()
如果我将函数 b1 添加到 D12 调用 D1::b1 一切都会编译并运行。

struct D12
: virtual public D1
, virtual public B2
{
void b1() override {D1::b1();}//would solve the problem, but is not feasible
void b2() override {}
};

不幸的是,在我的情况下,我无法更改类 D12,是否有另一种可能让它运行?
我认为编译器知道继承层次结构,所以他应该知道哪些函数在哪个继承级别是已知的/可访问的。但可能我遗漏了一些它不起作用的原因?

最佳答案

不要使用成员函数指针,或者获取完全正确的类型(无转换)。

真的,与成员函数指针分离。存储 T* 的元组在不使用 std::function 的情况下使用函数对象(如果您关心一个或几个字节,则通过私有(private)继承(启用空基优化)) .

所以

template<class T, class...Fs>

我们创建 std::tuple<Fs...> .我们通过遍历元组来执行(有很多关于这个的堆栈溢出问题,谷歌可以找到它们)。

我们可以用lambdas来描述调用成员函数,或者写一个template<class U, void(U::*mem)()>如果您不喜欢必须实际传递无状态对象,请使用无状态助手。

这里有一些 C++14 助手:

template<class=void,std::size_t...Is>
auto indexer( std::index_sequence<Is...> ) {
return [](auto&&f)->decltype(auto) {
return decltype(f)(f)( std::integral_constant<std::size_t, Is>{}... );
};
}
// takes a number N
// returns a function object that, when passed a function object f
// passes it compile-time values from 0 to N-1 inclusive.
template<std::size_t N>
auto indexer() {
return indexer( std::make_index_sequence<N>{} );
}
// takes a function object f
// returns a function object that takes any number of arguments
// and invokes `f` on each of them
template<class F>
auto for_each_arg(F&& f) {
return [f = std::forward<F>(f)](auto&&...args)->void {
// this is a bit insane. We want to expand our parameter pack
// args... in a way that we do it from left to right. As it happens,
// creating a C-style array is one of the legal ways to do this.
// So we create an anonymous C-style array, and discard it immediately
// The void stuff is a mixture of suppressing warnings and
// ensuring that if someone has a hostile `operator,` it doesn't cause
// any issues
// the result of this expression is an array of `int` full of `0`,
// plus the function `f` invokes on each of the `args...` in order:
using discard=int[];
(void)discard{0,(void(
f( decltype(args)(args) )
),0)...};
};
};

给定一个元组 bob lambdas,我们可以在某个指针上调用它们 p像这样:

// the pack 0 to N-1, where N is the size of bob:
auto index = indexer<std::tuple_size<decltype(bob)>{}>();

// From a compile time `i`, do what we want:
auto invoker = [&](auto i) {
std::get<i>(bob)(p);
};

// For each compile time integer from 0 to N-1,
// call invoker:
index(for_each_arg(invoker));

所有这些在 C++17 中变得更加容易。

上面的代码充满了微优化,其中一些使其更难理解。如果你想了解更多,请直接在这个主题上找到一个 SO 问题,或者如果你找不到一个问题。


上面的一些是C++14。在 C++11 中,我们必须手动扩展其中一些 lambda。

例如,indexer变成:

template<std::size_t...Is>
struct indexer_t {
template<class F>
auto operator()( F&& f ) const
-> decltype(std::forward<F>(f)( std::integral_constant<std::size_t, Is>{}... ))
{
return std::forward<F>(f)( std::integral_constant<std::size_t, Is>{}... );
}
};

template<class=void,std::size_t...Is>
indexer_t<Is...> indexer( std::index_sequence<Is...> )
{ return {}; }
template<std::size_t N>
auto indexer()
-> decltype( indexer(std::make_index_sequence<N>{}) ) )
{ return {}; }

或类似的东西。

一些名义上的 C++14 编译器可能也需要上述帮助(例如 MSVC2015),因为它们不允许您从 lambda 中的封闭上下文扩展参数包。

Live example #1Live example #2 .我用 std::array因为它类似于元组(支持 get 和元素计数特征),但 3 个元素的输入更少。

关于c++ - 如何使用来自不同继承层次级别的多个虚函数指针作为模板参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40575060/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com