我正在创建一个class C
,它继承自可变数量的类。定义了这些类的列表,例如:A,B
。在class C
的函数中,我需要从所有基类调用函数,但是对象可以是C<A,B>
,C<A>
或C<B>
,所以如果我要调用class A
的函数在C<B>
中,我会收到一个错误消息。这是类的示例以及我如何解决问题的示例:
class A
{
int a;
public:
virtual void set_a(const int &value)
{
a = value;
}
protected:
virtual int get_a()
{
return this->a;
}
};
class B
{
int b;
public:
virtual void set_b(const int &value)
{
b = value;
}
protected:
virtual int get_b()
{
return this->b;
}
};
template<class ...T>
struct Has_A
{
template<class U = C<T...>>
static constexpr bool value = std::is_base_of < A, U > ::value;
};
template<class ...T>
class C :
virtual public T...
{
public:
#define HAS_A Has_A<T...>::value
void f()
{
#if HAS_A<>
auto a = this->get_a();
#endif
auto b = this->get_b();
cout << HAS_A<>;
}
};
当我调用对象f()
的{{1}}时,它会跳过调用C<A,B>
,但输出为get_a()
。
最初,我写了这个
true
但是我不想为template<class U = C<T...>>
typename std::enable_if<!std::is_base_of<A, U>::value, int>::type get_a()
{
return -1;
}
template<class U = C<T...>>
typename std::enable_if<std::is_base_of<A,U>::value, int>::type get_a()
{
return A::get_a();
}
和A
的所有功能重写它。假设B
还有10个功能。
有什么漂亮的解决方案吗?
P.S对不起,我的英语。我以前从未使用过SFINAE。 基本上,我有一堆基因,我想为它们写一个方便的包装,在那里人们可以配置他希望有机体拥有的基因。
答案 0 :(得分:3)
在当前标准中,这很简单:
void f() {
if constexpr(Has_A<T...>::value) {
auto a = get_a();
}
auto b = get_b();
}
答案 1 :(得分:1)
我认为您可以使用function-member-pointer来做到这一点。
call_if_base
仅在baseT
是T
的基数的情况下才调用给定的函数指针。但是,所有函数结果都将被忽略,并且至少需要一个参数。
template <class baseT, class T, typename funcT, class ...Args>
typename std::enable_if<std::is_base_of<baseT, T>::value, void>::type call_if_base(T& obj, funcT func, Args... args) {
(dynamic_cast<baseT&>(obj).*func)(args...);
}
template <class baseT, class T, typename funcT, class ...Args>
typename std::enable_if<!std::is_base_of<baseT, T>::value, void>::type call_if_base(T& obj, funcT func, Args... args) {
}
template<class ...T>
class C :
virtual public T...
{
public:
void set(const int &value) {
call_if_base<A, C>(*this, &A::set_a, 0);
call_if_base<B, C>(*this, &B::set_b, 5);
}
};
或作为成员函数
template<class ...T>
class C :
virtual public T...
{
public:
void set(const int &value) {
call_if_base<A>(&A::set_a, 0);
call_if_base<B>(&B::set_b, 5);
}
protected:
template <class baseT, typename funcT, class ...Args>
typename std::enable_if<std::is_base_of<baseT, C>::value, void>::type call_if_base(funcT func, Args... args) {
(dynamic_cast<baseT&>(*this).*func)(args...);
}
template <class baseT, typename funcT, class ...Args>
typename std::enable_if<!std::is_base_of<baseT, C>::value, void>::type call_if_base(funcT func, Args... args) {
}
};
答案 2 :(得分:1)
如果可以使用C ++ 17,则bipll的解决方案(if constexpr ()
)是(IMHO)更好的解决方案。
否则,对于C ++ 11或C ++ 14,我不确定这是一个好主意,但我提出以下解决方案,因为在我看来,这很可笑(而且有点变态)。
首先,我提出了一个更通用的Has_A
isTypeInList
template <typename...>
struct isTypeInList;
template <typename X>
struct isTypeInList<X> : public std::false_type
{ };
template <typename X, typename ... Ts>
struct isTypeInList<X, X, Ts...> : public std::true_type
{ };
template <typename X, typename T0, typename ... Ts>
struct isTypeInList<X, T0, Ts...> : public isTypeInList<X, Ts...>
{ };
我还建议使用简单的indexSequence
template <std::size_t...>
struct indexSequence
{ };
启发std::index_sequence
(不幸的是)仅从C ++ 14开始可用。
因此,在C<T...>
内,您可以定义模板using
template <typename X>
using list = typename std::conditional<isTypeInList<X, Ts...>{},
indexSequence<0u>,
indexSequence<>>::type;
如果list<A>
是indexSequence<0>
可变参数列表的一部分,则A
为T...
,否则为indexSequence<>
(空序列)。
现在,您可以编写f()
来简单地调用一个辅助函数f_helper()
,该函数会收到多达indexSequence
个需要检查的类型。
举个例子:如果您需要知道A
和B
是否属于T...
可变参数列表的一部分,则必须按如下方式编写f()
void f ()
{ f_helper(list<A>{}, list<B>{}); }
现在f_helper()
可以是private
函数,并且可以是
template <std::size_t ... As, std::size_t ... Bs>
void f_helper (indexSequence<As...> const &,
indexSequence<Bs...> const &)
{
using unused = int[];
int a { -1 };
int b { -1 };
(void)unused { 0, ((void)As, a = this->get_a())... };
(void)unused { 0, ((void)Bs, b = this->get_b())... };
// do something with a and b
}
这个想法是,如果As...
在0
中,则A
为T...
。否则为空列表。
所以
int a { -1 };
使用伪造的a
的值初始化get_a()
。
使用
(void)unused { 0, ((void)As, a = this->get_a())... };
仅在a = this->get_a()
可变列表中(如果且仅当A
时才执行T...
)。
此解决方案的有趣之处在于,当a = this->get_a()
不在可变参数列表中时,A
并不是问题。如果As...
是一个空列表,则不存在。
以下是一个C ++ 11完整的工作示例(其中,我在Ts...
中将T...
的{{1}}可变序列重命名)
C