C++概念,检查可变参数模板中是否有重复类型

rkue9o1l  于 2023-04-08  发布在  其他
关注(0)|答案(5)|浏览(134)

我正试图弄清楚如何编写一个检查变量模板中没有重复类型的conecpt。
我知道我不能递归地调用一个概念本身,但如果我可以,我的解决方案会看起来像这样(忽略缺少停止条件):

#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 {}

我在标准库中找不到类型特征或概念来帮助我解决这个问题。有什么想法吗?

dgiusagp

dgiusagp1#

这似乎是工作(live demo):

#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
}
2mbi3lxu

2mbi3lxu2#

template <class...Ts>
concept is_non_repeating_list_ = (_type_not_in_list_<Ts, Ts...> && ...);

这种疯狂的结构解决了递归问题。
但也没什么用因为

template<is_non_repeating_list ... TYPE_LIST>

不像is_non_repeating_list<TYPE_LIST...>那样将TYPE_LIST传递给is_non_repeating_list,而是像is_non_repeating_list<TYPE_LIST>...那样。
也就是说,每个is_non_repeating_list从您测试的TYPE_LIST中获得 * 确切的一个 * 类型。
你能做到的

template<class...TYPE_LIST> requires is_non_repeating_list<TYPE_LIST...>

但是。

nuypyhwy

nuypyhwy3#

下面是一个使用fold表达式的非递归实现:

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中缺少的。

dba5bblo

dba5bblo4#

一种非标准的方法是先对convert each type to a string,然后应用相应的算法进行检查

#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();
}();
x4shl7ld

x4shl7ld5#

大多数解决这个问题的方法都不能很好地扩展类型列表的长度--编译时间将是二次的,甚至更糟,天真地遍历包而不是使用折叠表达式很容易在包的长度上是三次的。
这里有一种在编译时只在包长度上线性增长的方法,假设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包含重复的直接基类,这将是病态的。

相关问题