【问题标题】:STL Container constructed from other container (e.g. list from vector)从其他容器构造的 STL 容器(例如来自向量的列表)
【发布时间】:2013-09-19 18:55:04
【问题描述】:

在我的代码中,有时我需要从另一个容器构造一个容器。但是这些容器对象是不同类型的,所以我不能使用复制构造函数。我所做的看起来像这样(C++11,我使用统一初始化和构造函数委托):

std::vector<int> source {1, 3, 5, 7, 9};
std::list<int> destination (source.begin (), source.end ());

我意识到 std::list 可以有这样的构造函数,让事情变得更漂亮:

template </* ... */>
class list
{
public:
    template <class Container> list (const Container& container)
    : list (container.begin (), container.end ())
    {
    }

    /* ... */
};

不幸的是,我只能为我自己的课程这样做,而不是为 STL 课程。所以第一个问题,STL 和 Boost 中不存在这样的构造函数是有原因的吗?第二个问题,是否有一种安全的方法来为 STL 容器实现这样的 ctor,也许通过以某种方式定义转换运算符?

这个问题当然只是一个方便的问题,对代码或程序行为无关紧要。但它可以使代码看起来更具可读性:

std::vector<int> source {1, 3, 5, 7, 9};
std::list<int> destination = source;

【问题讨论】:

  • 我个人的印象是(我没有这方面的消息来源)设计委员会有意识地选择迭代器范围作为标准库容器的主要抽象——老实说,它们工作得很好并且很灵活。添加您描述的构造函数将使标准库更大更复杂,minimal 的好处。通过引入for (int i : myintvector) 构造,在 C++11 中修复了迭代器范围使代码可读性降低的非常常见的情况。
  • 我也非常强烈地感觉到,修改标准库以在代码中保存几个字符不是在可维护性方面的良好权衡!
  • 存在提升范围。算法需要一个范围(由容器构造,可以使用适配器等进行转换)。存在向标准库添加范围的提议,但尚未达成共识,被认为是低优先级
  • @us2012:嗯,标准 C++ 委员会正在努力将范围集成到标准 C++ 库中。我认为一个提议允许基于范围的初始化沿着基于范围的for 进入 C++14。当我说“标准 C++ 委员会”时,它实际上具有误导性:这项工作实际上是由个人完成的,而我还没有完成应有的工作以将范围纳入 C++17。
  • @galop1n:我不认为将范围添加到标准 C++ 库被认为是低优先级,但不幸的是,正确地执行它比仅仅说明目标要多得多。

标签: c++ c++11 stl constructor iterator


【解决方案1】:

您应该怀疑将一种容器类型的完整范围复制到另一种容器类型的代码。这是一个不常见的活动。

在 C++ 中,通常首选显式转换。通常不鼓励将隐式转换和赋值运算符从一种类型转换为另一种类型,因为它们有时会导致意外转换。

如果您确实需要在容器类型之间频繁转换,请尝试为转换定义显式帮助函数:

template<typename ToType, typename FromType>
ToType container_cast(const FromType& source)
{
    return ToType(source.begin(), source.end(), source.get_allocator());
}

那么你的例子就变成了:

std::vector<int> source {1, 3, 5, 7, 9};
auto destination = container_cast<std::list<int> >(source);

【讨论】:

  • 在这种情况下,我更喜欢使用现有的 begin/end 构造函数,因为 container_cast 不熟悉且不标准,可能会吓到人。但如果它使代码更具可读性,它可能是一个不错的选择。
  • 如果您还需要复制分配器,辅助函数可能会有一些优点。
  • 我认为暗中复制分配器比转换容器类型更可疑——如果有人向特定容器提供了特定分配器,那么在将其用于另一个容器之前应该三思而后行,而不仅仅是让它默认发生。无论如何,如果反对提议的构造函数的唯一论据是它允许令人惊讶的隐式转换,那么适当的响应是显式构造函数。
【解决方案2】:

c++14 中有一个提议,如果我没记错的话,可以将另一个容器作为“可遍历”直接传递给容器的构造函数。

提案的最新版本可以在here找到。

【讨论】:

    【解决方案3】:

    我认为问题在于,让容器从任何可迭代的事物中实例化会导致许多意想不到的行为和歧义。例如:

    auto mystring = std::string { "ABC" };
    auto mylist = std::list { mystring };
    

    mylist 应在此处实例化为仅包含一个元素的字符串列表(如当前 C++17 的情况),或作为三个字符“A”、“B”和“C”的列表',就像可迭代展开中的情况一样?显式展开展开可以巧妙地解决这个问题,即使编写起来有点乏味。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-15
      • 1970-01-01
      • 1970-01-01
      • 2021-12-09
      相关资源
      最近更新 更多