在c++ 11中编写一个元函数,从类型列表返回一个类型,该类型列表具有给定类型的定义类型

Write a meta-function returning a type from a type-list that has a typedef of a given type in C++11

本文关键字:类型 列表 一个 返回 定义 函数 c++      更新时间:2023-10-16

我遇到了这样一个场景,其中我在可变模板列表中有一个类列表,并且,给定一个类型(Target_),我希望在列表(ContainingClass_)中找到typedef s Target_作为ContainingClass_::Class的类。

这是我目前的蛮力实现:

#include <iostream>
#include <cstdlib>
#include <type_traits>
#include <tuple>
template <uint32_t ID_, class Class_>
struct ContainingClass
{
    using Class = Class_;
    static constexpr uint32_t id() { return ID_; }
};
// Get the index of a type in a type list, or -1 on error (So that we only get the important static_assert).
template <typename Target_, typename ...List_>
struct IndexOf : std::integral_constant<int, -1> {};
template <typename Target_, typename NotTarget_, typename ...List_>
struct IndexOf<Target_, NotTarget_, List_...> : std::integral_constant<std::size_t, 1 + IndexOf<Target_, List_...>::value> {};
template <typename Target_, typename ...List_>
struct IndexOf<Target_, Target_, List_...> : std::integral_constant<std::size_t, 0> {};
// Check if a type exists in a typelist.
template <typename Target_, typename... List_>
struct TypeExists;
template <typename Target_>
struct TypeExists<Target_> : std::false_type {};
template <typename Target_, typename... List_>
struct TypeExists<Target_, Target_, List_...> : std::true_type {};
template <typename Target_, typename NotTarget_, typename... List_>
struct TypeExists<Target_, NotTarget_, List_...> : TypeExists<Target_, List_...> {};
// **THE META-FUNCTION THAT THE QUESTION IS ABOUT**
// Find the ContaingClass that typedefs Target_ as "Class" inside of it.
template <class Target_, class ...ContainingClasses_>
struct ContainingClassFinder
{
    static_assert(TypeExists<Target_, typename ContainingClasses_::Class...>::value, "No ContainingClass found for Target_.");
    using ContainingClass = typename std::tuple_element<IndexOf<Target_, typename ContainingClasses_::Class...>::value, 
                                                        std::tuple<ContainingClasses_...>>::type;
};
using namespace std;
// Use the meta function to return the id of the ContainingClass that contains a type.
template <class Target_, class ...ContainingClasses_>
uint32_t get_containing_id(ContainingClasses_...)
{
    return ContainingClassFinder<Target_, ContainingClasses_...>::ContainingClass::id();
}
struct Foo {};
struct Bar {};
struct Test {};
struct NonExistent {};
int main()
{
    // Prove that the right class was found be printing its ID out.
    // Prints 2.
    cout << get_containing_id<Test>(ContainingClass<0, Foo>{}, ContainingClass<1, Bar>{}, ContainingClass<2, Test>{}) << endl;
    // Causes static_assert.
    //cout << get_containing_id<NonExistent>(ContainingClass<0, Foo>{}, ContainingClass<1, Bar>{}, ContainingClass<2, Test>{}) << endl;
    return EXIT_SUCCESS;
}

这样做的问题是,它依赖于std::tuple,并进行两次线性搜索:一次用于存在检查(使用第一个辅助元函数),另一次用于获取类型(使用std::tuple_element)。

理想情况下,我希望一次完成所有这些,不需要两个辅助元函数和std::tuple ;这可行吗?如果没有,请对我的实现进行任何改进。

注意:

1。此元函数将用作库的实现细节;

2。元函数的结果应该是一个类型,特别是目标类型所在的包含类型。获取类的id()只是显示元函数正在工作的一种简单方法。

3。通过元函数,我指的是模板化的结构体,就像在<type_traits>

中定义的那样

下面的解决方案如何?

没有std::tuple,如果我没有错的话,只有一个线性搜索。

#include <iostream>
#include <cstdlib>
#include <type_traits>
template <uint32_t, class Class_>
struct ContainingClass
 { using Class = Class_; };
struct Foo {};
struct Bar {};
struct Test {};
template <typename, typename ...>
struct getContType;
template <typename T>
struct getContType<T>
 { using type = void; };
template <typename T, typename CC0, typename ... CCs>
struct getContType<T, CC0, CCs...>
 { using type = typename std::conditional<std::is_same<T,
         typename CC0::Class>::value, CC0,
         typename getContType<T, CCs...>::type>::type; };
int main()
 {
   static_assert(std::is_same<ContainingClass<2, Test>,
                 getContType<Test, ContainingClass<0, Foo>,
                 ContainingClass<1, Bar>, ContainingClass<2, Test>>::type
                 >::value, "!");
   return EXIT_SUCCESS;
 }

[Update] This is working for me:

// marker for not-found types
struct none {
};
// fwd declaration
template <typename Target_,typename ...List_> struct scanner;
template <
  typename Target_,
  typename Head_, typename ...List_
>
struct scanner<Target_, Head_, List_...> {
  using found_type=typename std::conditional<
   std::is_same<Target_, Head_>::value, // or any other predicate
   Head_,
   // if not, search further
   typename scanner<Target_, List_...>::found_type
  >::type;
  // were are going to reset it anyway if in the end the found_type==none
  static constexpr int value=
     std::is_same<Target_, Head_>::value
   ? 0
   : scanner<Target_, List_...>::value+1
  ;
};
// need a recursion stopper for the scanner here.
template <typename Target_>
struct scanner<Target_> {
  using found_type=none;
  // were are going to reset it anyway if in the end the found_type==none
  static constexpr int value=-1;
};
template <typename Target, typename... List>
struct ContainingClassFinder {
private:
  using result=scanner<Target, List...>;
public:
  static constexpr int value=
      std::is_same<typename result::found_type, none>::value
    ? -1
    : result::value
  ;
  using found_type=typename std::enable_if<
     false==std::is_same<typename result::found_type, none>::value,
     typename result::found_type
  >::type;
};
/// brief test
struct dummy {
};
using good=ContainingClassFinder<
  int,
  long, dummy, int, char
>;
static_assert(std::is_same<int, good::found_type>::value, "Oops - good type failing");
static_assert(good::value==2, "Oops - good index failing");

using bad_notfound=ContainingClassFinder<
  int,
  long, dummy, float, char
>;
static_assert(bad_notfound::value==-1, "Ooops - bad type failing index");
// this should create a compilation error due to std::enable_if with a false condition
using non_existing=typename bad_notfound::found_type; // yeap, it does cause compilation error