[英]SFINAE and variadic template classes
我正在创建一个继承自可变数量class C
类。 定义了这些类的列表,例如: A,B
。 在class C
函数中,我需要从所有基类调用函数,但是对象可以是C<A,B>
, C<A>
或C<B>
所以如果我将在C<B>
调用class A
函数,我将得到一个错误。 以下是类的示例以及我如何尝试解决问题:
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<>;
}
};
当我调用对象C<A,B>
f()
时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
所有功能重写这个。 让我们假设A
还有10个函数。
有没有漂亮的解决方案?
PS抱歉我的英文。 我之前从未使用过SFINAE。 基本上我有一堆基因,我想为它们编写方便的包装,在那里可以配置他想要有机体的基因。
在目前的标准中,这是微不足道的:
void f() {
if constexpr(Has_A<T...>::value) {
auto a = get_a();
}
auto b = get_b();
}
我认为你可以用function-member-pointer做到这一点。
call_if_base
仅在baseT
是T
的基础call_if_base
调用给定的函数指针。 但是,所有功能结果都会被忽略,并且至少需要一个参数。
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) {
}
};
如果你可以使用C ++ 17,双工的解决方案( if constexpr ()
)是(恕我直言)更好的解决方案。
否则,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;
因此,如果A
是T...
variadic列表的一部分,则list<A>
是indexSequence<0>
否则为indexSequence<>
(空序列)。
现在你可以编写f()
来简单地调用一个辅助函数f_helper()
,它接收尽可能多的indexSequence
,你需要检查多少个类型。
例如:如果您需要知道A
和B
是否是T...
variadic列表的一部分,您必须编写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
}
这个想法是,如果A
在T...
,则As...
为0
否则为空列表。
所以
int a { -1 };
使用假冒get_a()
的值初始化a
。
同
(void)unused { 0, ((void)As, a = this->get_a())... };
执行a = this->get_a()
,只执行一次iff(当且仅当) A
在T...
variadic列表中。
这个解决方案的有趣之处在于,当A
不在可变参数列表中时, a = this->get_a()
不是问题。 如果As...
是一个空列表,那就不存在了。
以下是一个C ++ 11完整的工作示例(我已经在Ts...
重命名了Ts...
T...
C
可变序列)
#include <utility>
#include <iostream>
#include <type_traits>
class A
{
private:
int a;
public:
virtual void set_a (int const & value)
{ a = value; }
protected:
virtual int get_a ()
{ std::cout << "get_a()!" << std::endl; return this->a; }
};
class B
{
private:
int b;
public:
virtual void set_b (int const & value)
{ b = value; }
protected:
virtual int get_b ()
{ std::cout << "get_b()!" << std::endl; return this->b; }
};
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...>
{ };
template <std::size_t...>
struct indexSequence
{ };
template <typename ... Ts>
class C : virtual public Ts...
{
private:
template <typename X>
using list = typename std::conditional<isTypeInList<X, Ts...>{},
indexSequence<0u>,
indexSequence<>>::type;
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
}
public:
void f ()
{ f_helper(list<A>{}, list<B>{}); }
};
int main()
{
C<> c0;
C<A> ca;
C<B> cb;
C<A, B> cab;
std::cout << "--- c0.f()" << std::endl;
c0.f();
std::cout << "--- ca.f()" << std::endl;
ca.f();
std::cout << "--- cb.f()" << std::endl;
cb.f();
std::cout << "--- cab.f()" << std::endl;
cab.f();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.