c++ - 如何确保一个类型在模板参数中只出现一次?
问题描述
假设我有一个别名:
using bar = foo<string, string, int>;
如何确保“字符串”在参数中只出现一次?如果它出现不止一次,则抛出错误。
我做了一个函数来计算一个类型出现在参数中的次数,但未能实现这个想法。
template <class T>
constexpr std::size_t type_count_impl(std::size_t count = 0) {
return count;
};
template <class T, class T1, class... Types>
constexpr std::size_t type_count_impl(std::size_t count = 0) {
return type_count_impl<T, Types...>(count + (std::is_same<T, T1>::value ? 1 : 0));
};
template <class T, class... Types>
constexpr std::size_t type_count() {
return type_count_impl<T, Types...>();
};
解决方案
如果可变参数包中的任何类型重复,则会出现简单的编译时错误,这非常简单:
template <typename T> struct Base{};
template <typename... Ts> struct NoDuplicates : Base<Ts>... {
constexpr operator bool() const { return true; }
};
就是这样,如果你需要它,它的编译速度将比任何递归模板元编程、折叠表达式或类型特征方法更快。事实上,我知道在编译时没有更快的技术。这是有效的,因为一个类不允许从同一个基类继承两次。它继承自Base<T>
而不是 just的原因T
是,如果 T 是您无法继承的类型,例如原始整数值、数组或 void 等。
要使用:
template <typename... Ts>
class Foo {
static_assert(NoDuplicates<Ts...>{});
};
Foo<int, char, int> foo; // ERROR (see below)
<source>:3:34: error: duplicate base type 'Base<int>' invalid
3 | template <typename... Ts> struct NoDuplicates : Base<Ts>... {
现在,如果您不想要编译错误,但想要计算一个布尔值来指示是否有任何重复,它会稍微复杂一些,但也不算太糟糕。评论显示要检查的 3 个案例:
template <typename T, typename... Rest>
constexpr bool hasDuplicates() {
// Check T against each item in Rest, and if any match we have duplicates
if ((std::is_same_v<T, Rest> || ...))
return true;
// Is there anything left to check in Rest? If not, no duplicates.
if constexpr (sizeof...(Rest) == 0)
return false;
// T isn't duplicated, but is anything in Rest duplicated?
return hasDuplicates<Rest...>();
}
它可以类似地使用:
template <typename... Ts>
class Foo {
static_assert(not hasDuplicates<Ts...>());
};
Foo<int, std::string, std::string> foo; // Error, there are dupes
最后,如果您只关心特定类型是否重复,那就更容易了:
// Check if Needle is found 2 or more times in this Haystack
template <typename Needle, typename... Haystack>
constexpr bool hasDuplicateInList() {
return ((std::is_same_v<Needle, Haystack> + ...)) > 1;
}
就“抛出”而言,如果这是你想要的,如果你检测到布尔值在正常情况下具有不允许的值,你总是可以抛出异常if