如何在编译时订购类型
How to order types at compile-time?
考虑以下程序:
#include <tuple>
#include <vector>
#include <iostream>
#include <type_traits>
template <class T>
struct ordered {};
template <class... T>
struct ordered<std::tuple<T...>>
{
using type = /* a reordered tuple */;
};
template <class T>
using ordered_t = typename ordered<T>::type;
int main(int argc, char* argv[])
{
using type1 = std::tuple<char, std::vector<int>, double>;
using type2 = std::tuple<std::vector<int>, double, char>;
std::cout << std::is_same_v<type1, type2> << "n"; // 0
std::cout << std::is_same_v<ordered_t<type1>, ordered_t<type2>> << "n"; // 1
return 0;
}
ordered
助手必须在元组中重新排序类型,以便两个具有萨姆斯类型的元组,但订购的订购不同导致相同的元组类型:它可以是第一个,第二个,甚至是另一个元组:只需要具有相同的大小和相同的元素,但按独特的顺序(无论该顺序如何(。
是否可以使用模板元编程技术在编译时进行此操作?
困难的部分正在提出一种订购类型的方法。用谓词对类型列表进行排序是一件琐事,但可行。我将重点放在比较谓词上。
一种方法是创建一个类模板,该模板为每种类型定义一个唯一的ID。它有效,可以简单地编写:
template <typename T, typename U>
constexpr bool cmp() { return unique_id_v<T> < unique_id_v<U>; }
但是提出这些独特的ID是一个不一定可行的障碍。您是否将它们全部注册到一个文件中?那不是超级扩展。
如果我们可以...获取所有类型的 name 作为编译时间字符串的 name 。反思会给我们这一点,然后这个问题是微不足道的。在此之前,我们可以做一些更脏的事情:使用__PRETTY_FUNCTION__
。GCC和Clang在constexpr
上下文中使用该宏都可以,尽管它们具有不同的格式。如果我们的签名如下:
template <typename T, typename U>
constexpr bool cmp();
然后,GCC报告cmp<char, int>
为"constexpr bool cmp() [with T = char; U = int]"
,而Clang将其报告为"bool cmp() [T = char, U = int]"
。这是不同的...但是足够近,我们可以使用相同的算法。这基本上是:找出T
和U
在哪里,只是进行正常的字符串词典比较:
constexpr size_t cstrlen(const char* p) {
size_t len = 0;
while (*p) {
++len;
++p;
}
return len;
}
template <typename T, typename U>
constexpr bool cmp() {
const char* pf = __PRETTY_FUNCTION__;
const char* a = pf +
#ifdef __clang__
cstrlen("bool cmp() [T = ")
#else
cstrlen("constexpr bool cmp() [with T = ")
#endif
;
const char* b = a + 1;
#ifdef __clang__
while (*b != ',') ++b;
#else
while (*b != ';') ++b;
#endif
size_t a_len = b - a;
b += cstrlen("; U = ");
const char* end = b + 1;
while (*end != ']') ++end;
size_t b_len = end - b;
for (size_t i = 0; i < std::min(a_len, b_len); ++i) {
if (a[i] != b[i]) return a[i] < b[i];
}
return a_len < b_len;
}
有一些测试:
static_assert(cmp<char, int>());
static_assert(!cmp<int, char>());
static_assert(!cmp<int, int>());
static_assert(!cmp<char, char>());
static_assert(cmp<int, std::vector<int>>());
这不是最漂亮的实现,我不确定它是否有意义地批准了标准,但是它可以使您写下自己的作品,而无需手动并仔细注册所有类型。并在Clang和GCC上编译。所以也许足够好。
这是对Barry提出的方法的轻微修改,该方法与Visual Studio一起使用。而不是创建存储函数名称的编译时字符串:
template <typename T, typename U>
constexpr bool cmp()
此方法直接比较了Type_name&lt&lt;t> :: name((。当Macro __pretty_function__返回的类型T和U的名称被逗号分隔时,Barry的方法不起作用,因为逗号也可以分开模板参数,当t或u是类或功能模板时。
。// length of null-terminated string
constexpr size_t cstrlen(const char* p)
{
size_t len = 0;
while (*p)
{
++len;
++p;
}
return len;
}
// constexpr string representing type name
template<class T>
struct type_name
{
static constexpr const char* name()
{
#if defined (_MSC_VER)
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
};
};
// comparison of types based on type names
template<class T1, class T2>
constexpr bool less()
{
const char* A = type_name<T1>::name();
const char* B = type_name<T2>::name();
size_t a_len = cstrlen(A);
size_t b_len = cstrlen(B);
size_t ab_len = (a_len < b_len) ? a_len : b_len;
for (size_t i = 0; i < ab_len; ++i)
{
if (A[i] != B[i])
return A[i] < B[i];
}
return a_len < b_len;
}
// simple checks
template<class ... Type>
struct list;
static_assert(less<list<int, void, list<void, int>>, list<int, void, list<void, void>>>());
static_assert(less<list<int, void, list<void, int>>, list<int, void, list<void, int>, int>>());
此方法在VS上起作用。我不确定它在clang还是GCC上有效。
tl; dr:在编译时获取类型名称,然后订购。
我认为,以前的答案有点特殊 - 至少在实施中。
在这一点上,我们有一个非常不错的,多编译器支持的功能,可以作为字符串视图获取类型的名称作为编译时字符串。我在这里只引用其签名:
template <typename T>
constexpr std::string_view type_name();
这构成了从类型到编译时可弥补的值的注入映射。鉴于这些,您可以轻松地实现一个类似选择的过程,以获取每种类型的相对顺序。最后,您使用这些订单组装新的元组。
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 使用简单类型列表实现的指数编译时间.为什么
- 编译标准库类型
- 在没有定义返回类型的函数中返回布尔值,并将结果保存在无错误的char编译中-为什么
- 非类型指针和引用模板参数,以及在编译时如何/为什么解析它们.c++
- 使用 make 编译 MPI,几个命名空间错误,例如"错误:未知类型名称'使用'?
- C++ 编译错误:意外的类型名称"字符串":预期的表达式
- 为什么 std::make_shared 无法编译带有已删除运算符 new 的类型?
- 标量类型的特征模板无法编译固定大小的子矩阵操作
- 在其他容器中使用 boost::container::static_vector 时,GCC 编译错误"将'const s'绑定到类型's&'的引用丢弃限定符"
- 由于类型从 C 转换为 C++,无法编译错误 C2440
- 通过编译时值推断整数的类型
- 基于派生类型的编译时行为分支
- C++ 编译错误:gnu_printf是无法识别的格式函数类型
- 将函数类型作为模板参数传递不会编译
- 枚举类的基础类型别名为整型类型(编译错误)
- 无法将类型转换为类型*-C++编译错误
- 返回仅移动类型编译,即使复制构造函数不可用
- 如何根据模板中的类型编译函数
- 无法使用模板化类型编译 va_arg() 调用,因为模板参数中的逗号