首页 > 解决方案 > 如何确保一个类型在模板参数中只出现一次?

问题描述

假设我有一个别名:

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

标签: c++templates

解决方案


如果可变参数包中的任何类型重复,则会出现简单的编译时错误,这非常简单:

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


推荐阅读