在C++中避免别名(例如,将容器的元素添加到自身时)的"right"方法是什么?

What is the "right" way to avoid Aliasing (e.g. when adding an element of a container to itself) in C++?

本文关键字:C++ 是什么 方法 right 添加 元素 别名 例如      更新时间:2023-10-16
std::vector<int> a;
a.push_back(1);
a.push_back(a[0]);

我刚刚了解到上面的代码可能非常危险。

(如果原因不明显,你并不孤单...这对我来说也不明显。

我的问题:

  1. 处理它的"标准"方式是什么?创建一个新变量,然后立即将其分配给某些东西,这对我来说似乎有点奇怪。有没有更好的处理方法?

  2. 您如何训练自己注意这样的混叠问题?您在寻找什么模式?我不知道承认这种情况;当我了解 C 中的 restrict 关键字时,我才知道混叠,直到现在我才明白问题到底是什么。

<小时 />

编辑:

我很想接受一个答案,但似乎问题的第 (2( 部分没有得到回答。我想知道人们使用什么策略来定位他们编写的代码中的别名错误。

到目前为止,我想出的一种策略是避免在两个参数中传递相同的值。(在本例中,一个参数是隐式的,一个是显式的。

还有其他容易注意和注意的事情吗?

编辑:从技术上讲,如果包含的类型具有无抛掷复制构造函数,则标准并不要求这是正确的。我不知道任何实现无论如何都不成立,因为当通用的实现在所有情况下都同样有效时,它需要生成两个push_back实现。

混叠通常是一个问题,但在这种特殊情况下不是。代码:

assert( v.size() > 0 );
v.push_back( v[0] );

通过异常保证(这是不实现自己的容器的一个很好的理由,你可能不会正确实现它们(,通过标准(C++03(保证是正确的。特别是§23.1 [lib.container.requirements]/10指令:

除非另有说明(见23.2.1.3和23.2.4.3([注:这两个参考文献分别指dequevector insert]本条款中定义的所有容器类型都满足以下附加要求:

— 如果 push_back(( 或 push_front(( 函数抛出异常,则该函数没有影响

其中重要的一点是,如果在操作中抛出任何异常,容器保持不变,这意味着没有迭代器失效,这反过来意味着原始内存区域保持不变,直到保证不会抛出任何异常(析构函数的双关语除外(。由于在一般情况下,复制构造函数可以抛出,因此实现必须确保在销毁任何对象之前执行所有副本。

这在 C++0x 中变得更加明显,当对象不是从一个位置复制到另一个位置,而是移动时。由于新元素的副本可能会抛出,因此必须在执行任何移动之前执行该副本,否则您将处于原始容器中的某些对象已失效的情况。

我想这将是安全的:

std::vector<int> a(1);
a.push_back(1);
a.push_back(int(a[0]));

push_back(const T& el);实现中检查el是否在数组或其他内部存储器中。这是处理此类问题的唯一政治正确方法。

容器

应将其作为不同的容器处理 - 不同的安全规则。

这对您来说可能不是一个有用的答案,但恕我直言,"正确"的方法是容器类应该正确处理别名,这样调用者就不必担心它。 特别是,push_back(((或等效的(应执行以下操作:

// C++-ish pseudo-code, exception-safety left as an exercise for the reader
void push_back(const T & t)
{
   if (current_size == alloced_size)
   {
      // Oops, our data array is full.  Time to trade it in for a bigger one
      T * newArray = new T[alloced_size*2];
      copy_items(newArray, current_array, current_size);
      newArray[current_size++] = t;
      delete [] current_array;    // delete old array only AFTER all references to t
      current_array = new_array;
      alloced_size *= 2;
   }
   else current_array[current_size++] = t;
}

我只是在飞翔,所以请不要认为这是福音,但这行得通吗?

a.push_back(1);
a.push_back(&(new int(a[0])));