我正在尝试弄清楚如何编写一个概念来检查可变参数模板中是否没有重复类型。
我知道我不能在其自身内部递归地调用一个概念,但如果我可以的话,我的解决方案看起来像这样(忽略缺少停止条件):
#include <concepts>
template <class TYPE, class ... TYPE_LIST>
concept is_not_in_list = ((!std::same_as<TYPE, TYPE_LIST>) && ...);
template <class FIRST_TYPE_IN_LIST, class ... REST_OF_TYPE_LIST>
concept is_non_repeating_list_ = (_type_not_in_list_<FIRST_TYPE_IN_LIST, REST_OF_TYPE_LIST> && is_non_repeating_list<REST_OF_TYPE_LIST>);
// Usage
template<is_non_repeating_list ... TYPE_LIST>
class MyClass {}
我在标准库中找不到类型特征或概念来帮助我解决这个问题。有什么想法吗?
template <class...Ts>
concept is_non_repeating_list_ = (_type_not_in_list_<Ts, Ts...> && ...);
这种疯狂的结构解决了你的递归问题。
但这并没有多大帮助,因为
template<is_non_repeating_list ... TYPE_LIST>
不会像
TYPE_LIST
一样将is_non_repeating_list
传递给is_non_repeating_list<TYPE_LIST...>
,而是像is_non_repeating_list<TYPE_LIST>...
.
即,每个
is_non_repeating_list
从您测试的TYPE_LIST
中得到恰好一个类型。
你可以做到
template<class...TYPE_LIST> requires is_non_repeating_list<TYPE_LIST...>
然而。
你可能会尝试解决这个问题的大多数方法随着类型列表的长度而变小——编译时间将是二次的或更糟,天真地走包而不是使用折叠表达式很容易在类型列表的长度上是立方的包。
这是在编译时执行此操作的一种方法,它仅在包长度中线性增长,假设
std::make_index_sequence<N>
在 N
中是最坏的线性:
#include <utility>
template<typename T> struct type_base {};
template<int N, typename T> struct indexed_type_base : type_base<T> {};
template<typename Indexes, typename ...T> struct indexed_types;
template<std::size_t ...Indexes, typename ...T>
struct indexed_types<std::index_sequence<Indexes...>, T...> : indexed_type_base<Indexes, T>... {};
template<typename ...T>
concept is_non_repeating_list =
std::is_standard_layout_v<indexed_types<std::make_index_sequence<sizeof...(T)>, T...>>;
这里的技巧是
indexed_types
是一个标准布局类类型当且仅当它的所有基类都是不同的类型时,当且仅当类型包不包含重复项时才会发生这种情况。基类的索引序列和额外层仅用于避免indexed_types
包含重复的直接基类,这将是错误的。
这似乎有效(现场演示):
#include <type_traits>
// Check for a single matching type
template<typename T,
typename ...Args> struct does_not_repeat : std::true_type {};
template<typename T, typename U, typename ...Args>
struct does_not_repeat<T, U, Args...> :
std::conditional_t< !std::is_same_v<T, U> &&
does_not_repeat<T, Args...>::value,
std::true_type, std::false_type> {};
// Now, check each type to see if it matches any of the following types
template<typename ...Args> struct no_repeating_type : std::true_type {};
template<typename T,
typename ...Args>
struct no_repeating_type<T, Args...>
: std::conditional_t< does_not_repeat<T, Args...>::value &&
no_repeating_type<Args...>::value,
std::true_type, std::false_type> {};
// Specialization to pull the parameters out of some template
template<typename T> struct no_repeated_params;
template<template<typename ...> typename T,
typename ...Args>
struct no_repeated_params<T<Args...>> : no_repeating_type<Args...> {};
// And make it a concept
template<typename T>
concept unique_template_parameters = no_repeated_params<T>::value;
// Let's test this
template<unique_template_parameters T> void only_unique()
{
}
template<typename ...> struct sample {};
void testsuite()
{
only_unique<sample<int>>(); // OK
only_unique<sample<int, float>>(); // OK
only_unique<sample<int, int>>(); // ERROR
only_unique<sample<char, int, float, int>>(); // ERROR
}
这是一个使用折叠表达式的非递归实现:
template<typename ... args>
struct type_list{
constexpr static std::size_t size()
{ return sizeof...(args); };
};
tempalte<typename candidate, typename ... args>
constexpr std::size_t count(const type_list<args...>)
{ return (static _cast<std::size_t>(std::same_as<candidate,args>) + ...); };
template<typename ... candidates, typename ... args>
constexpr std::size_t count_list(const type_list<args...> tl, const type_list<candidates...>)
{ return (count<candidates>(tl) + ...); };
template<typename ... args>
constexpr std::size_t duplications =
count_list(type_list<args...>{}, type_list<args...>{});
template<typename ... args>
concept distinct = /*final untility:
total count of all types is the count of args,
or there's some duplications*/
(duplications<args...> == sizeof...(args));
这包括其他几个有用的实用程序,包括
type_list
,这是 STL 中缺少的。
一种非标准的方式是将每个类型转换为字符串,然后应用相应的算法进行检查
#include <algorithm>
#include <string_view>
#include <vector>
template<class... Types>
concept non_repeating_list = [] {
std::vector<std::string_view> types{type_name<Types>()...};
std::ranges::sort(types);
return std::ranges::adjacent_find(types) == types.end();
}();
这里其实有问题:(1)你是怎么定义概念的? (2) 你如何使用这个概念?
对于定义,使用 Boost.Mp11,这是一个简短的单行代码(一如既往):
template <class... Ts>
concept is_non_repeating_list = mp_is_set<mp_list<Ts...>>::value;
基本上,使用已经存在的算法即可。
但是使用方面也很重要。你写的是:
template <is_non_repeating_list... Ts> class MyClass {};
这意味着:
template <class... Ts>
requires (is_non_repeating_list<Ts> && ...)
class MyClass {};
这不是你想要的,因为这个要求是微不足道的(每个类型本身当然是一个唯一的类型列表)。你需要手动写出概念:
template <class... Ts>
requires is_non_repeating_list<Ts...>
class MyClass {};
现在实际上会一起检查所有类型,而不是单独检查每个类型。