C++通用插入到标准容器中

C++ generic insert into std container?

本文关键字:标准 插入 C++      更新时间:2023-10-16

如果我有以下程序:

#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,这将无法编译。

您可以使用表达式 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() 是有效的表达式,则首选第一个重载,因为int...更适合0 如果push_back()不是有效的表达式,则只有一个可行的重载。

<小时 />

这是否真的是一个好主意是一个单独的问题。

我相信

所有*C++容器(尽管不是像priority_queue这样的容器适配器)都有一个如下所示的插入版本:

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是唯一没有这种方法的人,这是有道理的。

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

已经有一个(#include <iterator>),但它的语法很奇怪:

std::inserter(container, container.end()) = value;

所有这一切都是在引擎盖下使用卢克莱蒂尔的答案,这仍然存在forward_list缺乏insert方法的问题。

如果您担心集合中插入元素的顺序,

template<typename Container, typename value>
void addelement(Container& C, value v)
{
    std::fill_n(std::inserter(C,C.end()), 1,v);
}
int main()
{
    std::vector<int> v;
    addelement(v, 2);
    addelement(v, 4);
    addelement(v, 6);
    std::set<int> s;
    addelement(s, 8);
    addelement(s, 6);
    addelement(s, 4);
    addelement(s, 8);
    std::cout << "Vector elements :: " << std::endl;
    for (auto item : v)
        std::cout << item << std::endl;
    std::cout << "Set elements :: " << std::endl;
    for (auto item : s)
        std::cout << item << std::endl;
    return 0;
}