简体   繁体   English

C++ - 指定模板值类型

[英]C++ - Specify template value type

I have the function我有 function

// Helper to determine whether there's a const_iterator for T.
template <typename T>
struct hasConstIt {
private:
    template<typename C> static char test(typename C::const_iterator*);
    template<typename C> static int test(...);
public:
    enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

// Check if a container contains an element.
template <typename Container, typename = std::enable_if_t<hasConstIt<Container>::value> >
bool contains(const Container & container, typename Container::value_type const & element) {
    return std::find(container.begin(), container.end(), element) != container.end();
}

How can I specify that the container value type must be a specific type?如何指定容器值类型必须是特定类型? Say I want the container to contain int , then valid containers could be vector<int> , deque<int> etc.假设我希望容器包含int ,那么有效容器可以是vector<int>deque<int>等。

You can get it done by using the std::is_same_v ans static_assert .您可以使用std::is_same_vstatic_assert来完成它。

template <typename Container>
bool contains(const Container& container, typename Container::value_type const& element) {
    static_assert(std::is_same_v<typename Container::value_type, int>, "The container 'value_type' must be 'int'!");

    // You don't have to explicitly check if there is a 'const_iterator' because the STL containers specify 'cbegin()' and 'cend()'
    // which returns the 'Container::const_iterator'. So checking if these functions are there will be enough (for STL containers).
    return std::find(container.cbegin(), container.cend(), element) != container.cend();
}

Now if the Container::value_type is not int , it'll throw a compiler error stating that The container 'value_type' must be 'int'!现在如果Container::value_type不是int ,它会抛出一个编译器错误,指出The container 'value_type' must be 'int'!

Bonus: Your hasConstIt can be written in a more better way (more readable and the IDE wont complain that the functions are undefined),奖励:您的hasConstIt可以用更好的方式编写(更具可读性,IDE 不会抱怨函数未定义),

template <typename T>
struct hasConstIt {
private:
    template<typename C> static constexpr bool test(typename C::const_iterator*) { return true; }
    template<typename C> static constexpr bool test(...) { return false; }
public:
    static constexpr bool value = test<T>(nullptr);
};

Now you can use another static_assert to check if there's a const_iterator for the Container explicitly.现在您可以使用另一个static_assert来明确检查Container是否有const_iterator This step is optional.此步骤是可选的。

You can use std::is_same to check if a type is exaclty the same as a specified one.您可以使用std::is_same检查类型是否与指定的类型完全相同。 If you want to check both of your conditions at once, you can simply use logical operators in the std::enable_if conditon:如果您想同时检查两个条件,您可以简单地在std::enable_if条件中使用逻辑运算符:

template <typename Container, typename = std::enable_if_t<
    (std::is_same<typename Container::value_type, int>::value &&
    hasConstIt<Container>::value)
    > >
bool contains(const Container & container, typename Container::value_type const & element) {
    return std::find(container.begin(), container.end(), element) != container.end();
}

Contrary to the other answer, this is SINAE friendly.与其他答案相反,这是 SINAE 友好的。 But if you only want to make sure it does not compile if the user makes a mistake I'd go with the other solution because it will give you way better error messages.但是,如果您只想确保在用户犯错时它不会编译,我会选择 go 和其他解决方案,因为它会为您提供更好的错误消息。

Example here .这里的例子。

If you can use C++20 you can use requires to constrain your template types.如果您可以使用 C++20,则可以使用requires来约束您的模板类型。 This is both SFINAE-friendly and gives you a nice error message.这既是 SFINAE 友好的,又会给你一个很好的错误信息。

template<typename Container>
requires (
    requires {
     typename Container::const_iterator; 
     typename Container::value_type; 
    } 
    && std::same_as<typename Container::value_type, int>
)
bool contains (const Container & container, typename Container::value_type const & element) {
    return true;
}

Or, if you need to constrain multiple functions you can define a concept或者,如果您需要约束多个功能,您可以定义一个概念

template<typename Container>
concept good_container = 
  requires(Container c) {        
   typename Container::const_iterator; 
   typename Container::value_type;                 
  } 
  && std::same_as<typename Container::value_type, int>;

template<good_container Container>
bool contains (const Container & container, typename Container::value_type const & element) {
    return true;
}

Example here .这里的例子。

hasConstIt looks redundant here. hasConstIt在这里看起来是多余的。 It can be simplified using the std::void_t trick .可以使用std::void_t 技巧对其进行简化。 You can just write:你可以写:

template <typename Container, typename = std::void_t<typename Container::const_iterator>>
bool contains(/* ... */);

If Container has const_iterator member type, it will be converted into void by std:void_t , if it doesn't, SFINAE will step in. This is a general technique, but in this particular case you can go further and omit std::void_t :如果Container具有const_iterator成员类型,它将被std:void_t转换为void ,如果没有,SFINAE 将介入。这是一种通用技术,但在这种特殊情况下,您可以进一步 go 并省略std::void_t

template<typename Container, typename = typename Container::const_iterator>
bool contains(/* ... */);

To embed an additional constraint on the element type you can just add another dummy template parameter in the standard way:要在元素类型上嵌入附加约束,您只需以标准方式添加另一个虚拟模板参数:

template<typename Container,
         typename = typename Container::const_iterator,
         typename = std::enable_if_t<std::is_same_v<typename Container::value_type, int>>>
bool contains(/* ... */);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM