繁体   English   中英

如何使用通用模板函数来处理具有不同成员的对象?

[英]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特性。 if constexpr更容易实现C ++ 17的引入; 但它是可行的。

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是一个真正的样式的,并且所述第二参数(或[](auto&&...){}如果没有第二个参数)如果some_test是假像型的。

然后我们编写代码来检测你的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 );

现在,当且仅当m.myArray有效时,才会运行中间的lambda。

可以编写更复杂的测试,检查不仅仅是存在,但上述通常就足够了。

在像MSVC 2015这样的非C ++ 11编译器中,替换

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_t<blah>{}替换index<blah>

auto&& lambdas的缺乏使得上述非常痛苦; 您可能必须将lambda转换为外联函数对象。 然而,自动lambda是人们实现的第一个C ++ 14特性之一,通常在他们完成C ++ 11之前。

上面的代码是固体设计的,但可能包含拼写错误。

有一种方法可以使用一个通用函数吗?

我不这么认为,因为如果你在这个函数中插入一个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,但在这种情况下(IMHO)是更好的标签分派。 因此,您可以将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_traitsiostream

如果您不想修改实例,重载在您的情况下工作正常:

#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成员数据。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM