将 std::type_identity 对象转换为类型

Converting std::type_identity object to a type

假设我们创建两个 type_of 函数 return std::type_identity, like:

template<auto VAR>
auto type_of() {
    return std::type_identity<decltype(VAR)>{};
}

template<typename T>
auto type_of() {
    return std::type_identity<T>{};
}

std::type_identity获取实际类型的方法似乎有点麻烦:

// this works
// both i1 and i2 are ints
decltype(type_of<int>())::type i1;
decltype(type_of<int{}>())::type i2;

有没有办法在上面的表达式中放弃 decltype 的需要,或者把它放在一个可重用的表达式中,以获得更好的效果,比如:

// can this work?
type_of<int>()::type i1;
type_of<int{}>()::type i2;

甚至更好:

// can this work??
type_of_t<int> i1;
type_of_t<int{}> i2;

注意:类型和非类型模板参数的特化,这可能是一个方向,不起作用(无法编译):

template<auto>
struct type_of;

template<typename T>
struct type_of<T> { // <== compilation error: type/value mismatch 
    using type = T;
};

template<auto VAR>
struct type_of<VAR> {
    using type = decltype(VAR);
};

您可以创建类型别名。但是你不能“超载”它。所以我的解决方案是创建两个:

template <auto Var>
using type_of_var_t = decltype(type_of<Var>())::type;

template <class T>
using type_of_t = decltype(type_of<T>())::type;

auto test()
{
    type_of_var_t<11> i1 = 24;
    type_of_t<int> i2 = 17;

}

std::type_identity 对象获取类型 can be encapsulated into the following expresion:

template<auto x>
using type = typename decltype(x)::type;

这将允许替换繁琐的表达式:

decltype(type_of<int>())::type i1;
decltype(type_of<int{}>())::type i2;

用更简单的表达方式:

type<type_of<int>()> i1;
type<type_of<int{}>()> i2;

还是需要经过两步type_of然后type)作为第一步应该能够得到一个 typevariable,它只适用于函数模板重载,那么函数不能 return类型,因此它 return 是一个需要模板表达式来提取内部类型的对象。


根据您要对类型执行的操作,代码可以变得更加简单。

如果您只想创建该类型的对象,您可以将对象的创建转发到函数中:

template<auto VAR, typename... Args>
auto create_type_of(Args&&... args) {
    return decltype(VAR){std::forward<Args>(args)...};
}

template<typename T, typename... Args>
auto create_type_of(Args&&... args) {
    return T{std::forward<Args>(args)...};
}

auto i1 = create_type_of<int>(7);
auto i2 = create_type_of<int{}>(7);

以这种方式从 std::type_identity can work 创建类型的一般情况:

template<auto VAR>
constexpr auto type_of() {
    return std::type_identity<decltype(VAR)>{};
}

template<typename T>
constexpr auto type_of() {
    return std::type_identity<T>{};
}

template<typename T, typename... Args>
auto create(std::type_identity<T>, Args&&... args) {
    return T{std::forward<Args>(args)...};
}

auto i1 = create(type_of<int>(), 7);
auto i2 = create(type_of<int{}>(), 7);

请注意,整个过程仅适用于可用作 non-type 模板参数的变量。有关更通用的方法,无需模板(但使用宏......),因此可以用于不能作为模板参数的变量,请参阅

在 C++ 中,模板参数必须是值、类型或另一个模板(它本身必须适合声明的模板头)。一定是其中之一。

如果你想这样做:

the idea is to get something that is unaware on the caller side whether the template parameter is a type or a variable

能够做到这一点的基本要求是编写一个模板,其参数可以是值类型。

C++ 不允许这样做。

模板函数重载允许您摆脱类似的东西。但这之所以有效,是因为它不是一个模板。这是 两个 模板超载。选择哪一个取决于提供的模板参数。

模板类 不能重载。并且模板专业化不能改变原始模板的性质(比如它的模板参数是什么)。它只能让你重新解释原来的模板参数的模板参数,以便提供一个替代的实现。

如果你想要这个,你将不得不等到 C++ 能够拥有一个可以是任何东西的模板参数,或者直到 C++ 能够将类型转换为值并返回(即:反射).

根据设计,C++ 模板中的模板参数是模板、类型或值。

在模板中,您知道它们是什么。模板中依赖于模板参数的所有表达式都使用 typenametemplate 关键字(或上下文)消除歧义,因此在替换参数之前知道它们的类别。

现在有元编程库可以与 value-substitutes 一起使用 3.

template<class T> struct type_tag_t {using type=T;};
template<class T> constexpr type_tag_t<T> tag={};
template<template<class...>class Z> struct template_z_t {
  template<class...Ts>
  using result = Z<Ts...>;
  template<class...Ts>
  constexpr result<Ts...> operator()( type_tag_t<Ts>... ) const { return {}; }
};
template<template<class...>class Z>
constexpr template_z_t<Z> template_z = {};

此处模板映射到 constexpr 函数对象。 template_z 模板可让您将 type-templates 映射到此域。

template<auto x>
using type = typename decltype(x)::type;

template<auto x>
constexpr std::integral_constant<std::decay_t<decltype(x)>, x> k = {};

所以,

constexpr auto vector_z = template_z<std::vector>;
constexpr auto vector_tag = vector_z( tag<int>, tag<std::allocator<int>> );

然后你可以回到类型:

type<vector_tag> v{1,2,3,4};

这可能不是您想要的。

您可能愿意查看 Universal Template Parameters

的提案

蕴含的示例与您同时专攻 TTP 和 NTTP 的用例非常匹配:

template <template auto>
struct X;
template <typename T>
struct X<T> {
   // T is a type
   using type = T;
};
template <auto val>
struct X<val> : std::integral_constant<decltype(val), val> {
   // val is an NTTP
};