C ++泛型插入到std容器中?

时间:2016-05-22 19:24:18

标签: c++ c++11 containers

如果我有以下程序:

#include <vector>
#include <set>

template<class T, class U>
void AddToContainer(T& container, U value)
{
  container.push_back(value);
}

int main(char**, int)
{
   std::vector<int> v;
   AddToContainer(v, 1);

   std::set<int> s;
   AddToContainer(s, 1);

   return 0;
}

如何将容器的添加设为通用?由于std::set没有push_back但只有insert,因此无法编译。

3 个答案:

答案 0 :(得分:5)

您可以将表达式SFINAE与伪参数一起使用来检查push_back()是否有效:

template <class C, class V>
auto append(C& container, V&& value, int)
    -> decltype(container.push_back(std::forward<V>(value)), void())
{
    container.push_back(std::forward<V>(value));
}

template <class C, class V>
void append(C& container, V&& value, ...)
{
    container.insert(std::forward<V>(value));
}

您的功能将转发到:

template <class C, class V>
void AddToContainer(C& container, V&& value) {
    append(container, std::forward<V>(value), 0);
}

如果push_back()是有效的表达式,则首选的重载将是首选,因为int0的匹配比...更好,如果push_back() isn&# 39; ta有效表达,然后只有一个可行的过载。

这实际上是不是一个好主意是一个单独的问题。

答案 1 :(得分:3)

我相信所有* C ++容器(尽管不是诸如priority_queue这样的容器适配器)都具有如下所示的insert版本:

iterator insert(iterator location, T&& value)

对于序列集合,位置为实际位置;对于关联的集合(例如mapunordered_map),迭代器是一个“提示”参数(例如,如果您已经确切知道某个元素所属的位置,则它可以帮助map快速插入该元素排序)。但是,提供无效提示不会导致任何无效行为,因此C ++集合的有效通用插入将为:

template<C, T>
void insert(C& collection, T&& value) {
    collection.insert(collection.end(), std::forward<T>(value));
}

*看来forward_list是唯一没有此方法的方法,这很有意义。

答案 2 :(得分:1)

C ++ 20样式:

template<typename C, typename V>
  requires requires (C& c, V&& v) { c.push_back(std::forward<V>(v)); }
auto AddToContainer(C& container, V&& value) {
  return container.push_back(std::forward<V>(value));
}

template<typename C, typename V>
  requires (requires (C& c, V&& v) { c.insert(c.end(), std::forward<V>(v)); } &&
    !requires(C& c, V&& v) { c.push_back(std::forward<V>(v)); })
auto AddToContainer(C& container, V&& value) {
  return container.insert(container.end(), std::forward<V>(value));
}

更简洁但诊断更差:

template<typename C, typename V>
auto AddToContainer(C& container, V&& value)
{
  if constexpr (requires (C& c, V&& v) { c.push_back(std::forward<V>(v)); })
    return container.push_back(std::forward<V>(value));

  else
    return container.insert(container.end(), std::forward<V>(value));
}