C++ 转换需要特别注意集合

C++ 转换需要特别注意集合,c++,stl,stl-algorithm,C++,Stl,Stl Algorithm,我不明白为什么这段代码会编译: #include <set> #include <list> #include <algorithm> int modify(int i) { return 2*i; } int main (int args, char** argv) { std::set<int> a; a.insert(1); a.insert(2); a.insert(3); std::list<int>

我不明白为什么这段代码会编译:

#include <set>
#include <list>
#include <algorithm>

int modify(int i)
{
 return 2*i;
}

int main (int args, char** argv)
{
  std::set<int> a;
  a.insert(1);
  a.insert(2);
  a.insert(3);

  std::list<int> b;  // change to set here

  std::transform(a.begin(), a.end(), b.begin(), modify);   // line 19
}
为什么呢?我想原因可能与set是一个关联容器,而list是一个序列容器有关,但我可能完全偏离了这一点

编辑
我忘了提到:我使用默认标准(c++98)在gcc 3.4.2和llvm 3.3上尝试了这一点。我使用c++03在llvm 3.3上再次尝试,得到了相同的行为。

首先,现有代码显示未定义的行为,因为目标列表实际上没有空间。使用
back\u插入器
边走边创建空间

至于集合,集合的元素是不可变的。这就是为什么即使您有空间,也不能分配给取消引用的迭代器。但是使用插入器是非常好的。

这个记录

std::transform(a.begin(), a.end(), b.begin(), modify);
<> >即使是<代码> STD::列表< /C>(虽然它可以编译为STD::List或STD::设置在C++ 2003中,集合有非const迭代器),因为您定义了一个空列表。
std::list<int> b;
因为他们被重新分配了。
因此,如果要考虑一个集合,那么假设集合已经有了所有必需的元素,但是你不能改变它们。当您使用迭代器适配器std::insert_迭代器时,它会在容器中添加新元素。因此,在这种情况下,您可以使用一个集合。

在没有
std::inserter
的代码中,
transform
分配给
*b.begin()
。在
set
的情况下,这是对元素的常量引用(从C++11开始)。因此,出现编译时错误

列表
的情况下,它仍然分配给
*b.begin()
,它进行编译,但由于列表的大小为0,因此具有未定义的行为。因此,
b.begin()
不能被取消引用

这与
set
是一个关联容器而
list
是一个序列这一事实有关,这是正确的。关联容器不允许修改用作键的元素部分。在
set
的情况下,该部分是元素,对于
map
可以修改值,但不能修改键


std::inserter
的整个要点是,它不通过迭代器进行赋值,而是在C++03中调用
insert
,这段代码会编译,但会导致未定义的行为-您不能简单地更改set的值,因为它们必须按升序排列。在c++11中,它是固定的,
set::iterator
const T
上的双向迭代器,因此您根本无法更改它的值
std::inserter
不会更改现有值,而是在
operator++
中插入新值,因此一切正常

查看
std::set::iterator
的类型。但是,如果列表没有任何空间来放置转换的元素,则列表也无法工作。@UndefinedBehavior您有未定义的行为。对我来说,它在C++03中也无法编译,至少使用llvm 3.3是这样。
std::list<int> b;
[b.begin(), b.begin() + distance( a.begin(), a.end() ) )