12

下面std::move()的代码在 Visual Studio 2013(带有调试配置)中编译时会发出运行时警告,因为它检测destnullptr. 但是,源范围是空的,因此dest永远不应访问。C++ 标准可能不清楚这是否应该被允许?它声明: 要求:结果不得在 [first,last) 范围内。 Anullptr似乎满足该要求。

#include <vector>
#include <algorithm>

int main() {
    std::vector<int> vec;
    int* dest = nullptr;
    // The range [begin(vec),end(vec)) is empty, so dest should never be accessed.
    // However, it results in an assertion warning in VS2013.
    std::move(std::begin(vec), std::end(vec), dest);
}
4

3 回答 3

17

不仅需要满足Requires:子句,还需要满足Effects:Returns:子句中的所有内容。让我们来看看它们:

效果:将范围内的元素复制到从 开始[first,last)到 的范围内。[result,result + (last - first))firstlast

first == last,则范围[result, result + 0)必须是有效范围。

[iterator.requirements.general]/p7 状态:

范围[i,i)是一个空范围;...Range [i,j)有效当且仅当j可从i.

并且同一部分的 p6 指出:

当且ji当存在++i 使i == j.

从这些段落中,我得出结论:

int* dest = nullptr;

然后[dest, dest)形成一个有效的空范围。所以Effects:段落中的第一句话对我来说看起来不错:

对于每个非负整数n < (last - first),执行*(result + n) = *(first + n)

没有非负整数n < 0,因此不能进行赋值。所以第二句不禁止dest == nullptr

回报: result + (last - first)

[expr.add]/p8 特别允许将 0 加到任何指针值上,结果比较等于原始指针值。因此dest + 0是等于 的有效表达式nullptrReturns:子句没有问题。

要求: result不得在范围内[first,last)

我认为没有合理的方法可以解释dest“在”空范围内。

复杂性:确切last - first的任务。

这证实了不能完成任何任务。

我在标准中找不到任何声明,使这个例子除了格式良好之外什么都没有。

于 2013-10-20T21:55:41.703 回答
-1

Visual Studio 中的 STL 调试版本会进行额外的参数验证。在这种情况下,它正在验证 dest 不为空,因为它不应该是,这是失败的。发布版本可能会按照您的预期运行,从不使用 dest 但这不会使输入数据有效。

STL 的调试版本试图通过说“您的输入不好”来帮助您。虽然在某些情况下错误的输入可能不是问题,但验证器无法知道您在什么条件下传递了错误的数据。就个人而言,我宁愿让 VS 告诉我调试版本中的错误输入,也不愿在生产中抛出运行时异常。

当然,你可能会做这样的事情:

int* dest = nullptr;
if (vec.size() > 0) dest = realDest;
std::move(std::begin(vec), std::end(vec), dest);

但是验证器不知道,所以它假设最坏的情况,特别是因为修复它对您来说非常容易(只需始终传递一个有效的输出迭代器)并且不警告您它可能会在生产运行时对您的应用程序产生可怕的后果.

于 2013-10-20T18:06:57.170 回答
-1

这个答案来自@Philipp Lenk 的评论。如果他提供了答案并且您认为可以接受,请选择他的而不是我的,并请对他的原始评论进行投票。

§25.1.5:在本条款中,模板参数的名称用于表达类型要求 [...] 如果算法的模板参数是 OutputIterator、OutputIterator1 或 OutputIterator2,则实际模板参数应满足输出迭代器的要求.

§24.2.4:如果 X 满足迭代器要求并且表 108 中的表达式有效且具有指示的语义,则类或指针类型 X 满足输出迭代器的要求。表的第一行:*r = o附注post: r is incrementable.

int* dest = nullptr;
*dest = 5;
++dest;

上述代码无效。在这种情况下,您不能分配给*dest,因此根据标准,您不能为 dest 传递 nullptr。

于 2013-10-20T18:25:39.333 回答