在嵌套typename上生成模板的通用方法



动机:

给定一个类层次结构(并将CRTP技术与mixin-tp.classes一起使用,但为了简单起见,这里省略了它),我想用已知的标识符来通用地处理嵌套类型,但可能使用";未知";父类。

我第一次尝试(不成功)的最小例子:

#include <type_traits>
using namespace std;
struct A {
using Type_x = int;
using Type_y = char;
};
struct B {
using Type_x = float;
using Type_y = double;
};
template <typename T> struct C {
using Type_x = typename T::Type_x;
using Type_y = typename T::Type_y;
};
// not possible
// template <typename T, typename U> using Tp_type = typename T::typename U;
//
// static_assert(is_same_v<C<A>::Type_x, Tp_type<C<A>, Type_x>>);

我想出了一个变通的解决方案,它看起来很实用,但并不太漂亮。这里有一个例子,派生类还引入了进一步的嵌套类型,但它仍然以一种非常通用的方式处理:

/// given the code above ...
struct None {};
constexpr int type_x_id = 1;
constexpr int type_y_id = 2;
template <typename T>
struct D1 : C<T> {
using typename C<T>::Type_x;
using typename C<T>::Type_y;
template <int typeId>
using Type = conditional_t<typeId == type_x_id, Type_x,
conditional_t<typeId == type_y_id, Type_y,
None>>;
};
constexpr int type_z_id = 3;
template <typename T>
struct D2 : D1<T> {
using Type_z = long;
template <int typeId>
using Type = conditional_t<!is_same_v<typename D1<T>::template Type<typeId>, None>,
typename D1<T>::template Type<typeId>,
conditional_t<typeId == type_z_id, Type_z,
None>>;
};
template <typename T, int typeId> using Tp_type = typename T::template Type<typeId>;

用途/测试:

static_assert(is_same_v<D1<A>::Type_x, Tp_type<D1<A>, type_x_id>>);
static_assert(is_same_v<D1<A>::Type_y, Tp_type<D1<A>, type_y_id>>);
// static_assert(!is_same_v<D1<A>::Type_z, Tp_type<D1<A>, type_z_id>>);
static_assert(is_same_v<D2<A>::Type_x, Tp_type<D1<A>, type_x_id>>);
static_assert(is_same_v<D2<A>::Type_y, Tp_type<D1<A>, type_y_id>>);
static_assert(!is_same_v<D2<A>::Type_z, Tp_type<D1<A>, type_z_id>>);
static_assert(is_same_v<D1<A>::Type_x, Tp_type<D2<A>, type_x_id>>);
static_assert(is_same_v<D1<A>::Type_y, Tp_type<D2<A>, type_y_id>>);
// static_assert(!is_same_v<D1<A>::Type_z, Tp_type<D2<A>, type_z_id>>);
static_assert(is_same_v<D2<A>::Type_x, Tp_type<D2<A>, type_x_id>>);
static_assert(is_same_v<D2<A>::Type_y, Tp_type<D2<A>, type_y_id>>);
static_assert(is_same_v<D2<A>::Type_z, Tp_type<D2<A>, type_z_id>>);
static_assert(is_same_v<D1<B>::Type_x, Tp_type<D1<B>, type_x_id>>);
static_assert(is_same_v<D1<B>::Type_y, Tp_type<D1<B>, type_y_id>>);
// static_assert(!is_same_v<D1<B>::Type_z, Tp_type<D1<B>, type_z_id>>);
static_assert(is_same_v<D2<B>::Type_x, Tp_type<D1<B>, type_x_id>>);
static_assert(is_same_v<D2<B>::Type_y, Tp_type<D1<B>, type_y_id>>);
static_assert(!is_same_v<D2<B>::Type_z, Tp_type<D1<B>, type_z_id>>);
static_assert(is_same_v<D1<B>::Type_x, Tp_type<D2<B>, type_x_id>>);
static_assert(is_same_v<D1<B>::Type_y, Tp_type<D2<B>, type_y_id>>);
// static_assert(!is_same_v<D1<B>::Type_z, Tp_type<D2<B>, type_z_id>>);
static_assert(is_same_v<D2<B>::Type_x, Tp_type<D2<B>, type_x_id>>);
static_assert(is_same_v<D2<B>::Type_y, Tp_type<D2<B>, type_y_id>>);
static_assert(is_same_v<D2<B>::Type_z, Tp_type<D2<B>, type_z_id>>);
static_assert(!is_same_v<D1<A>::Type_x, Tp_type<D1<B>, type_x_id>>);
static_assert(!is_same_v<D1<A>::Type_y, Tp_type<D1<B>, type_y_id>>);
// static_assert(!is_same_v<D1<A>::Type_z, Tp_type<D1<B>, type_z_id>>);
static_assert(!is_same_v<D2<A>::Type_x, Tp_type<D1<B>, type_x_id>>);
static_assert(!is_same_v<D2<A>::Type_y, Tp_type<D1<B>, type_y_id>>);
static_assert(!is_same_v<D2<A>::Type_z, Tp_type<D1<B>, type_z_id>>);
static_assert(!is_same_v<D1<A>::Type_x, Tp_type<D2<B>, type_x_id>>);
static_assert(!is_same_v<D1<A>::Type_y, Tp_type<D2<B>, type_y_id>>);
// static_assert(!is_same_v<D1<A>::Type_z, Tp_type<D2<B>, type_z_id>>);
static_assert(!is_same_v<D2<A>::Type_x, Tp_type<D2<B>, type_x_id>>);
static_assert(!is_same_v<D2<A>::Type_y, Tp_type<D2<B>, type_y_id>>);
static_assert(is_same_v<D2<A>::Type_z, Tp_type<D2<B>, type_z_id>>);

有更好的解决方案吗?例如,不需要每次出现新的嵌套类型标识符时都定义Type

如果不编写整个库或使用编译时反射(在这一点上看起来像c++26),就无法传递名称并基于它们查找类型。

但是您希望能够在编译时传递标识符。您的int解决方案有点尴尬;我建议传递进行查找的模板:

template<class T>
using Type_x_t = typename T::Type_x;
template<class T>
using Type_y_t = typename T::Type_y;

etc

然后你可以做:

template <typename T, template<class...> class U_t>
using Tp_type_t = U_t<T>;
static_assert(
std::is_same_v<D1<A>::Type_x, Tp_type_t<D1<A>, Type_x_t>);
);

或:

static_assert(
std::is_same_v<D1<A>::Type_x, Type_x_t<D1<A>>);
);

我们绕过将T映射到T::Foo的模板Foo_t<>;name";对于一个子类型。