如何使用通用模板函数来处理不同成员的对象?
How to use generic template function to handle objects with different members?
我已经环顾四周寻找解决方案,但是,我可能不知道我想要完成的确切定义或语言语法,所以我决定发布。
我有一些对象/结构,如下所示:
struct A
{
char myChar;
bool hasArray = false;
};
template <uint8_t ARRAY_LEN>
struct AA : public A
{
hasArray = true;
uint8_t myArray[ARRAY_LEN];
};
我想创建一个通用函数,它可以接受这两种对象类型,并为派生的struct AA
执行常见工作和特定工作。如下所示:
template <typename T>
void func(T (&m))
{
if (T.hasArray)
{
// do some processing with m.myArray
std::cout << sizeof(m.myArray) << std::endl;
// ...
}
// common processing
std::cout << "myChar: " << m.myChar << std::endl;
};
我希望能够像这样调用函数:
A a;
AA aa;
func(a); // compiler error, this would not work as no array member
func(aa); // this works
当然,这只是一个说明我意图的例子,但它总结了我想做的事情。实际代码要复杂得多,并且涉及更多的对象。我知道我可以重载,但我想知道是否有办法用一个泛型函数来实现?另请注意,我理解为什么编译器抱怨示例代码,我想知道是否有解决方法或我缺少的其他一些 c ++ 功能。我不想做任何类型的铸造... - 使用 c++11 和 GCC 4.8.5
这是一个C++14特征,具有相当大的复杂性。 C++17 引入了if constexpr
,使这更容易;但这是可行的。
template<std::size_t I>
using index_t=std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index{};
constexpr inline index_t<0> dispatch_index() { return {}; }
template<class B0, class...Bs,
std::enable_if_t<B0::value, int> =0
>
constexpr index_t<0> dispatch_index( B0, Bs... ) { return {}; }
template<class B0, class...Bs,
std::enable_if_t<!B0::value, int> =0
>
constexpr auto dispatch_index( B0, Bs... ) {
return index< 1 + dispatch_index( decltype(Bs){}...) >;
}
template<class...Bs>
auto dispatch( Bs... ) {
using I = decltype(dispatch_index( decltype(Bs){}... ));
return [](auto&&...args)->decltype(auto){
return std::get<I::value>( std::make_tuple(decltype(args)(args)..., [](auto&&...){}) );
};
}
dispatch( some_test )
返回一个需要auto&&...
的 lambda 。 如果some_test
是 true-like 类型,则返回第一个参数,如果some_test
是 false-like 类型,则返回第二个参数(如果没有第二个参数,则返回[](auto&&...){}
)。
然后,我们编写代码来检测您的myArray
。
namespace details {
template<template<class...>class Z, class=void, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = typename details::can_apply<Z, void, Ts...>::type;
template<class T>
using myArray_type = decltype( std::declval<T>().myArray );
template<class T>
using has_myArray = can_apply< myArray_type, T >;
如果T
有一个成员.myArray
,has_myArray<T>
是真的。
我们将这些连接在一起
dispatch( has_myArray<T>{} )(
[&](auto&& m) {
// do some processing with m.myArray
std::cout << sizeof(m.myArray) << std::endl;
// ...
}
)( m );
现在,中间的 lambda 运行当且仅当m.myArray
有效。
可以编写更复杂的测试,检查不仅仅是存在,但上述通常就足够了。
在非 C++11 编译器(如 MSVC 2015)中,将
std::enable_if_t<B0::value, int> =0
和
std::enable_if_t<!B0::value, int> =0
跟
class = std::enable_if_t<B0::value>
和
class = std::enable_if_t<!B0::value>, class=void
分别。 是的,这些更丑陋。 去和MSVC编译器团队谈谈。
如果您的编译器缺少 C++14,则必须编写自己的void_t
并编写自己的enable_if_t
或使用enable_if
的丑陋的较长版本。
此外,模板变量index
在 C++11 中是非法的。 将index<blah>
替换为index_t<blah>{}
。
缺少auto&&
lambda 使得上述工作非常痛苦;您可能必须将 lambda 转换为外联函数对象。 但是,人们在完成 C++11 之前实现的前 C++14 功能之一的自动 lambda。
上面的代码是可靠的设计,但可能包含拼写错误。
如果您不想修改实例,重载在您的情况下工作得很好:
#include<iostream>
#include<cstdint>
struct A
{
char myChar;
};
template <uint8_t ARRAY_LEN>
struct AA : public A
{
uint8_t myArray[ARRAY_LEN];
};
void func(const A &m)
{
std::cout << "myChar: " << m.myChar << std::endl;
};
template <uint8_t AL>
void func(const AA<AL> &m)
{
std::cout << sizeof(m.myArray) << std::endl;
func(static_cast<const A &>(m));
}
int main() {
func(A{});
func(AA<1>{});
}
如果你仍然想使用模板函数和一些sfinae,我可能会使用这样的东西:
#include<iostream>
#include<cstdint>
struct A
{
char myChar;
};
template <uint8_t ARRAY_LEN>
struct AA : public A
{
uint8_t myArray[ARRAY_LEN];
};
void func(A &m)
{
std::cout << "myChar: " << m.myChar << std::endl;
}
template <typename T>
auto func(T &m) -> decltype(m.myArray, void())
{
std::cout << sizeof(m.myArray) << std::endl;
A &a = m;
func(a);
}
int main() {
AA<1> aa{};
A a{};
func(a);
func(aa);
}
请注意,在这两种情况下,您实际上都不需要hasArray
成员数据。
一种方法可以用一个泛型函数来做到这一点吗?
我不这么认为,因为如果在此函数中插入sizeof(m.myArray)
,则无法使用没有myArray
成员的类型调用它。即使它位于运行时未执行的代码部分中,因为编译器需要编译它。
但是,如果我理解正确,您的hasArray
会说您的结构是否有myArray
成员。所以我想你可以在static constexpr
成员中转换它,如下所示
struct A
{
static constexpr bool hasArray { false };
char myChar { 'z' };
};
template <uint8_t ARRAY_LEN>
struct AA : public A
{
static constexpr bool hasArray { true };
uint8_t myArray[ARRAY_LEN];
};
现在,在func()
中,您可以调用第二个函数func2()
来选择两种情况:myArray
或不myArray
。您可以为此使用 SFINAE,但(恕我直言)在这种情况下是更好的标签调度。因此,您可以将hasArray
转换为不同的类型
template <typename T>
void func2 (T const & m, std::true_type const &)
{ std::cout << sizeof(m.myArray) << ", "; }
template <typename T>
void func2 (T const &, std::false_type const &)
{ }
template <typename T>
void func(T (&m))
{
func2(m, std::integral_constant<bool, T::hasArray>{});
// common processing
std::cout << "myChar: " << m.myChar << std::endl;
}
现在,您可以使用这两种类型调用func()
int main()
{
A a;
AA<12U> aa;
func(a); // print myChar: z
func(aa); // print 12, myChar: z
}
请记住包括type_traits
和iostream
.
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 在运行时有条件地删除类成员或跳过调用该成员对象的构造函数
- C++是否有定义的方法来传递指向类的成员对象的成员函数的指针
- (2 问题)"类"类型重新定义(即使 #pragma 一次),以及静态函数内的静态成员对象初始化?
- 声明成员对象而不调用其默认构造函数
- 指向成员对象的指针 - 中断线程
- 是否有更好的方法来封装成员对象可以访问的共享存储池?
- 应该在成员对象上调用析构函数
- 调用成员对象的构造函数
- 将 const 类型引用对象注册为类成员对象C++
- 修改类 c++ 中的成员对象
- 从成员对象调用方法
- 从对象调用成员对象,错误:引用非常量值的初始值必须是左值
- 构造函数,成员对象
- 不能使外部类成为内部类内的成员对象
- 使用从另一个类继承的类的对象初始化成员对象
- 如何避免需要在初始化列表中初始化成员对象
- 初始化具有参数的类成员对象的正确方法
- 将指针添加到成员对象中的指针动态阵列
- C 将成员对象函数分配给类成员功能