编写一个元函数,从C++11中具有给定类型的typedef的类型列表中返回类型

编写一个元函数,从C++11中具有给定类型的typedef的类型列表中返回类型,c++,templates,c++11,variadic-templates,template-meta-programming,C++,Templates,C++11,Variadic Templates,Template Meta Programming,我遇到了一个场景,在这个场景中,我在一个可变模板列表中有一个类列表,并且,给定一个类型Target,我希望在列表中找到包含class的类,该类将Target定义为ContainingClass::class 以下是我当前的暴力实施: #include <iostream> #include <cstdlib> #include <type_traits> #include <tuple> template <uint32_t ID_, cl

我遇到了一个场景,在这个场景中,我在一个可变模板列表中有一个类列表,并且,给定一个类型Target,我希望在列表中找到包含class的类,该类将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并进行两次线性搜索:一次使用第一个helper元函数进行存在性检查,另一次使用std::tuple_元素获取类型

理想情况下,我希望一次完成所有这些,而不需要两个helper元函数和std::tuple;这实际吗?如果没有,对我的实现的任何改进都将不胜感激

注:

一,。此元函数将用作库的实现细节;提振不是一种选择

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


三,。通过元函数,我指的是一个模板结构,就像在

[Update]中定义的那样,这对我来说是有效的:

// 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

[更新]这对我有用:

// 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

下面的解决方案怎么样

没有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;
 }

下面的解决方案怎么样

没有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;
 }


与其重新发明,不如看看Boost.Hana?@KerrekSB谢谢你提醒我;我忘了提到这是库的一部分,boost根本不是一个选项。请参阅我的最新答案:编译并按照我的意图工作,至少不要重新发明,可以看看boost。Hana?@KerrekSB谢谢提醒我;我忘了提到这是库的一部分,boost不是一个选项。请参阅我的更新答案:编译并按照我的预期工作至少这看起来很有希望,但获取id只是为了表明找到了正确的类型。元函数应该有一个类型结果,特别是在其中找到目标类型的包含类型的结果。@rationalcoder-我明白了。。。好函数可以返回一个类型的对象;不是一种类型;我修改了我的解决方案,以返回一个ContainingClass类型的对象;如果你只对类型感兴趣,我想你应该创建一个包含类型的结构,而不是函数。通过元函数,我本质上是指模板结构,就像在type_traits中定义的那样。我将更新我的问题以澄清这一点。我的意思和这个家伙一样:它现在就在那里。它在底部的注释中。如果您检查代码中的注释,我已经将ContainingClassFinder标记为有问题的元函数。@rationalcoder-不确定是否理解,但。。。添加了一个结构getContType;这就是你要找的?在main中添加了第二个static_assert来检查它。这看起来很有希望,但获取id只是为了表明找到了正确的类型。元函数应该有一个类型结果,特别是在其中找到目标类型的包含类型的结果。@rationalcoder-我明白了。。。好函数可以返回一个类型的对象;不是一种类型;我修改了我的解决方案,以返回一个ContainingClass类型的对象;如果你只对类型感兴趣,我想你应该创建一个包含类型的结构,而不是函数。通过元函数,我本质上是指模板结构,就像在type_traits中定义的那样。我将更新我的问题以澄清这一点。我的意思和这个家伙一样:它现在就在那里。它在底部的注释中。如果您检查代码中的注释,我已经将ContainingClassFinder标记为有问题的元函数。@rationalcoder-不确定是否理解,但。。。添加了一个结构getContType;这就是你要找的?在main中添加了第二个static_断言来检查它。这看起来像是我最初失败的尝试,哈哈。我会把它弄得乱七八糟,看看它是否适合我的需要。@rationalcoder更新/升级到仍然有效的东西作为一个例子,无法评估是否适合你的目的这看起来像我最初的尝试,我失败了,哈哈。我会把它弄得乱七八糟的,看看它是否适合我的需要。@rationalcoder更新/升级到仍然可以工作的东西作为一个例子,无法评估它是否适合您的目的