C++ 如何在编译时对类型排序?
考虑以下计划:C++ 如何在编译时对类型排序?,c++,types,tuples,template-meta-programming,c++17,C++,Types,Tuples,Template Meta Programming,C++17,考虑以下计划: #include <tuple> #include <vector> #include <iostream> #include <type_traits> template <class T> struct ordered {}; template <class... T> struct ordered<std::tuple<T...>> { using type = /*
#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;
}
#包括
#包括
#包括
#包括
模板
结构有序{};
模板
结构有序
{
使用type=/*重新排序的元组*/;
};
模板
使用ordered_t=typename ordered::type;
int main(int argc,char*argv[])
{
使用type1=std::tuple;
使用type2=std::tuple;
std::cout最难的部分是找到一种对类型进行排序的方法。通过谓词对类型列表进行排序是一件麻烦事,但也是可行的。我将在这里重点介绍比较谓词
一种方法是创建一个类模板,该模板为每种类型定义一个唯一的id。这样做可以使比较器易于编写:
template <typename T, typename U>
constexpr bool cmp() { return unique_id_v<T> < unique_id_v<U>; }
然后gcc将cmp
报告为“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;
}
constexpr size\u t cstrlen(const char*p){
尺寸长度=0;
而(*p){
++len;
++p;
}
回程透镜;
}
模板
constexpr bool cmp(){
const char*pf=\uuuu PRETTY\u FUNCTION\uuuuuu;
常量字符*a=pf+
#如果有声音__
cstrlen(“bool cmp()[T=”)
#否则
cstrlen(“constexpr bool cmp()[with T=”)
#恩迪夫
;
常量字符*b=a+1;
#如果有声音__
而(*b!=',')++b;
#否则
而(*b!=';')++b;
#恩迪夫
尺寸a_len=b-a;
b+=cstrlen(“;U=”);
常量字符*end=b+1;
而(*end!=']')++end;
尺寸b_len=end-b;
对于(大小i=0;i
通过一些测试:
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>>());
static_断言(cmp());
静态断言(!cmp());
静态断言(!cmp());
静态断言(!cmp());
静态_断言(cmp());
它不是最漂亮的实现,我也不确定它是否得到了标准的认可,但它允许您编写排序,而无需手动仔细注册所有类型。它在和上编译。因此,它可能已经足够好了。这是对Barry提出的方法的一个轻微修改,该方法适用于Visual StudioStudio。而不是创建存储函数名称的编译时字符串:
template <typename T, typename U>
constexpr bool cmp()
模板
constexpr bool cmp()
此方法直接比较由type\U name:name()返回的两种类型的名称。当宏\uu PRETTY\U FUNCTION\uuuu返回的类型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>>());
//以null结尾的字符串的长度
constexpr size\u t cstrlen(const char*p)
{
尺寸长度=0;
而(*p)
{
++len;
++p;
}
回程透镜;
}
//表示类型名称的constexpr字符串
模板
结构类型名称
{
静态constexpr const char*name()
{
#如果已定义(\u MSC\u VER)
返回函数;
#否则
返回函数;
#恩迪夫
};
};
//基于类型名的类型比较
模板
constexpr bool less()
{
const char*A=type_name::name();
const char*B=type_name::name();
尺寸a长度=cstrlen(a);
尺寸b_len=cstrlen(b);
大小为(a_len
此方法适用于VS。我不确定它是否适用于Clang或GCC。tl;dr:在编译时获取类型名称,并按其排序。
在我看来,以前的答案有点特殊——至少在实现上是这样
在这一点上,我们有一个非常好的多编译器支持,用于获取类型名称作为编译时字符串,作为字符串视图。我在这里只引用它的签名:
template <typename T>
constexpr std::string_view type_name();
模板
constexpr std::string_视图类型_name();
这构成了从类型到编译时可比较值的内射映射。给定这些值,您可以轻松实现一个类似过程来获取每个类型的相对顺序。最后,您可以使用这些顺序组装一个新元组。可能吗?是的。无论您得到什么答案,Andrei Alexandrescu都在“现代C++设计”的方式回到2001。细节无疑会有所不同,但核心思想是相同的。@ StayytLe:如果是什么,代码> T!= T2< /代码>但是<代码> siZeof(T1)= sisiof(T2)
?您将如何对它们进行唯一排序,以使std::is_same
按预期工作?@Nawaz-处理任何其他类型数据的方法相同。如果库不能,则强制用户提供任意排序。这不是不可能的。可以像添加一个或两个模板专门化一样简单。@StoryTeller:这就可以实现算法对于一个怪物,因为它很难提供模板特化,或者对于每一对大小相等的这样的对,那么问题是,为什么人们会使用这样的Meta FAN来开始?为什么不手动地这样做?在这个区域中似乎缺少C++。在编译时应该有一种排序类型的方法。编译时间O。运算符,相当于类型的std::less
。@Vincent:这整个问题可能是XY问题吗?如果您只需要比较两个元组的等价性,那么可能就不需要对它们进行排序-使用类型别名技术进行搜索,即使n^2可能比排序两个集合和compa更快在boost::hana
中,类型的表示是相等的static_断言(boost::hana::type_c==boost::hana::type_c);
,
template <typename T>
constexpr std::string_view type_name();