10

我正在寻找一种方法来提供一个函数,该函数采用模板化 (STL) 容器,但要求其元素为某种类型(例如int)。

这些函数调用应该是有效的:

std::vector<int> Argument;
void foo( Argument );

std::list<int> Argument
void foo( Argument );

std::deque<int> Argument
void foo( Argument );

...etc

这些函数调用应该是无效的:

std::vector<float> Argument;
void foo( Argument );

std::list<double> Argument
void foo( Argument );

std::deque<char> Argument
void foo( Argument );

...etc

有没有办法模板化“foo”以便int接受容器,但不接受具有不同元素类型的容器?

最好的,本

4

5 回答 5

13

使用标准库语义:

  • 将一对迭代器传递给foo,而不是容器:它使您的函数更加通用
  • 用于std::iterator_traits<Iterator>::value_type获取值类型
  • static_assert迭代器的值类型为int(或您想要的任何类型)

例子 :

#include <list>
#include <vector>

template<typename Iterator>
void foo(Iterator begin, Iterator end)
{
    static_assert(std::is_same<int, typename std::iterator_traits<Iterator>::value_type>::value, 
                                "Invalid value type : must be int");
}

int main() {
    std::list<int> l1;
    std::vector<int> v1;

    foo(std::begin(l1), std::end(l1)); // OK
    foo(std::begin(v1), std::end(v1)); // OK

    std::vector<float> v2;
    foo(std::begin(v2), std::end(v2)); // Doesn't compile
}

现场演示

笔记:

  • 如果 foo需要访问容器的特定成员函数(如 Deduplicator 所述,这可能是出于性能原因),那么您可能需要坚持使用以下Container参数:

示例:(请注意获取 的区别value_type,正如 MooingDuck 所指出的,这是使其与数组一起使用所必需的):

template <typename Container>
void foo(const Container& c)
{

    static_assert(std::is_same<int, std::iterator_type<decltype(std::begin(c))>::value_type>::value, "Invalid value type : must be int");

   // Use c member function(s)
}
于 2014-08-20T20:24:51.193 回答
9

STL 容器有typedef value_type,所以你可以使用它。

然后你可以禁止static_assert

template <typename Container>
void foo(const Container& )
{
    static_assert(std::is_same<int, typename Container::value_type>::value, "expect int type");
}

或通过 SFINAE

template <typename Container>
typename std::enable_if<std::is_same<int, typename Container::value_type>::value>::type
foo(const Container& )
{
}
于 2014-08-20T20:22:17.863 回答
4

您可以简单地使用 Sfinae,如下所示:

#include <type_traits>
#include <utility>

template <typename Container>
typename std::enable_if< std::is_same< typename Container::value_type, int >::value,
void >::type foo(Container& c) {
  /* code here */
};

如果容器没有与“int”相同的值类型,它将从“foo”的重载集中删除。您还可以使用其他特征,例如is_convertible更容易接受相关类型。使用没有 int 作为值的容器调用 foo 将被编译器报告为没有合适的重载候选者。

如果你没有 C++11 支持,我上面使用的东西可以在 Boost 中作为 C++98/03 的替代品使用。

于 2014-08-20T20:24:58.067 回答
3

另一种解决方案,使用模板模板参数

template<template<typename, typename...> class Container, typename... Params>
void foo(Container<int, Params...> const&)
{
  ...
}

这将匹配vectorlist或者deque只要容器中元素的类型是int.

现场演示

于 2014-08-20T20:37:16.013 回答
0

假设您想要一个可迭代的范围:

template<class I>
using value_type_t=typename std::iterator_traits<I>::value_type;

在 C++14 中已过时:

template<class T>using decay_t=typename std::decay<T>::type;
template<bool b,class T=void>using enable_if_t=typename std::enable_if<b,T>::type;

然后:

namespace adl_details{
  using std::begin;
 template<class C>using iterator_type=decay_t<decltype(begin(std::declval<C>()))>;
}
using adl_details::iterator_type;


template<class C>
enable_if_t<std::is_same<int, value_type_t<iterator_type_t<C>>>::value>
foo(C&& c){
}

有那个属性。

于 2014-08-20T23:01:45.947 回答