编译时c++函数,检查所有模板参数类型是否唯一



有一个很好的问题(在要求子句中不允许哪些替换失败?)提出了下一个问题。

需要编写一个编译时函数template<typename... Ts> constexpr bool allTypesUnique(),如果所有参数类型都是唯一的,则返回true,否则返回false。限制是不能成对比较参数类型。不幸的是,答案只解释了为什么不能用某种特定的方法实现这样的功能。

我认为解决方案可以实现使用多重继承。我们的想法是创建一个继承自多个类的类:Ts中的每种类型T都有一个。每个这样的类都定义了一个虚函数,其签名依赖于T。如果某些TTs中被发现不止一次,那么子类中的f函数将覆盖基类中的函数,并且它可以被检测到:

template<typename> struct A{};
template<typename T, typename... Ts>
struct B : B<Ts...> {
using B<Ts...>::f;
constexpr virtual void f(A<T>, bool & unique) { if( ++count > 1 ) unique = false; }
int count = 0;
};
template<typename T>
struct B<T> {
constexpr virtual void f(A<T>, bool & unique) { if( ++count > 1 ) unique = false; }
int count = 0;
};
template<typename... Ts>
constexpr bool allTypesUnique() {
B<Ts...> b;
bool res = true;
( b.f( A<Ts>{}, res ), ... );
return res;
}
int main() {
static_assert( allTypesUnique<void>() );
static_assert( allTypesUnique<void, int&>() );
static_assert( !allTypesUnique<int&, int&>() );
static_assert( allTypesUnique<char, short, int>() );
static_assert( !allTypesUnique<char, short, char>() );
}

演示:https://gcc.godbolt.org/z/8jhnE7P11

只是好奇,解决方案是正确的,有没有更简单的解决方案?

在支持类大小优化的编译器中,可以通过c++ 20的空成员属性[[no_unique_address]]获得更简单的解决方案。如果所有的类空成员都有不同的类型,那么它的sizeof将为1。如果某些成员类型是重复的,则它们不能共享相同的地址,并且sizeof将大于1。

解决方案代码:

template<typename> struct A{};
template<typename T, typename... Ts>
struct B : B<Ts...> {
[[no_unique_address]] A<T> a;
};
template<typename T>
struct B<T> {
[[no_unique_address]] A<T> a;
};
template<typename... Ts>
constexpr bool allTypesUnique() {
if constexpr (sizeof...(Ts) <= 1 )
return true;
else
return sizeof(B<Ts...>) == 1;
}
int main() {
static_assert( allTypesUnique<void>() );
static_assert( allTypesUnique<void, int&>() );
static_assert( !allTypesUnique<int&, int&>() );
static_assert( allTypesUnique<char, short, int>() );
static_assert( !allTypesUnique<char, short, char>() );
}

演示:https://gcc.godbolt.org/z/577EP1774

可以构造一个constexpr函数来回答这个问题:

template <class T, class... Ts>
constexpr bool unique_types()
{
if constexpr (sizeof...(Ts)) {
return !(std::is_same_v<T,Ts> || ...) && unique_types<Ts...>();
}
return true;
}

演示上面的代码在编译时相当于

for (int i(0); i < n; ++i)
for (int j(i + 1); j < n; ++j)
{ /* check type[i] vs type[j] */ }

如果你想在比较中有任何变化(例如认为intint&相同),只需修改std::is_same_v(例如std::is_same_v<std::decay_t<T>, std::decay_t<Ts>>)

如果根据每个给定类型使用虚拟基类,则将为结果类中的每个唯一类型获得一个基类实例。如果给定类型的数量是生成的基类的数量,则每个类型都是唯一的。你可以"测量"。生成的基类的数量由其大小决定,但必须注意,虚函数表指针内部的大小依赖于实现。因此,每个生成的类型应该足够大,以隐藏对齐问题。

BTW:它也适用于引用类型。


template < typename T> struct AnyT { char i[128]; };
template < typename FIRST, typename ... T>
struct CheckT: virtual AnyT<FIRST>, virtual CheckT<T...> { };
template < typename FIRST >
struct CheckT<FIRST>: virtual AnyT<FIRST> {};

template < typename ... T>
constexpr bool allTypesUnique()
{
using T1 = CheckT<int>;
using T2 = CheckT<bool, int>;
constexpr std::size_t s1 = sizeof( T1 );
constexpr std::size_t s2 = sizeof( T2 );
constexpr std::size_t diff = s2 - s1; 
constexpr std::size_t base = s1 - diff;
constexpr std::size_t measure = sizeof( CheckT< T...> );
return !((sizeof...(T)*diff+base) - measure);
}

int main() {
static_assert( allTypesUnique<void>() );
static_assert( allTypesUnique<void, int>() );
static_assert( !allTypesUnique<void, void>() );
static_assert( allTypesUnique<char, short, int>() );
static_assert( !allTypesUnique<char, short, char>() );
}

演示

我认为您可以尝试详细说明使用编译时类型id。下面是部分实现的c++ 14版本(需要编译时排序):

#include <cstddef>
#include <type_traits>
#include <utility>
namespace detail {
template<typename T>
struct type_id {
constexpr static int value{};
};
template<const int *const A, const int *const B>
struct same_address : std::false_type {};
template<const int *const A>
struct same_address<A, A> : std::true_type {};
}// namespace detail
// TODO: implement
template<const int *const... I>
struct sort_array {
using type = std::integer_sequence<const int *const, I...>;
};
template<typename>
struct find_duplicates;
template<const int *const A, const int *const B, const int *const... I>
struct find_duplicates<std::integer_sequence<const int *const, A, B, I...>> {
constexpr static bool value = std::conditional_t<detail::same_address<A, B>::value,
std::true_type,
find_duplicates<std::integer_sequence<const int *const, B, I...>>>::value;
};
template<>
struct find_duplicates<std::integer_sequence<const int *const>> {
constexpr static bool value = false;
};
template<const int *const I>
struct find_duplicates<std::integer_sequence<const int *const, I>> {
constexpr static bool value = false;
};
template<typename... T>
constexpr bool all_types_unique() {
return !find_duplicates<typename sort_array<&detail::type_id<T>::value...>::type>::value;
};
int main() {
static_assert(detail::same_address<&detail::type_id<int>::value,
&detail::type_id<int>::value>::value,
"");
static_assert(!detail::same_address<&detail::type_id<int>::value,
&detail::type_id<double>::value>::value,
"");
static_assert(all_types_unique<>(), "");
static_assert(all_types_unique<int>(), "");
static_assert(all_types_unique<int, double>(), "");
static_assert(all_types_unique<int, double, char>(), "");
static_assert(!all_types_unique<int, int>(), "");
static_assert(!all_types_unique<int, int, int>(), "");
return 0;
}

https://godbolt.org/z/E4G6YchE5

相关内容

最新更新