should std::copy() or std::move() of empty range r

2019-06-16 00:03发布

The std::move() in the code below issues a runtime warning when compiled in Visual Studio 2013 (with Debug configuration) because it detects that dest is a nullptr. However, the source range is empty, so dest should never be accessed. The C++ standard may be unclear as to whether this should be allowed? It states: Requires: result shall not be in the range [first,last). A nullptr would seem to satisfy that requirement.

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

3条回答
戒情不戒烟
2楼-- · 2019-06-16 00:12

The debug build of STL in Visual Studio does additional parameter validation. In this case, it is validating that dest is not null since it shouldn't be, which is failing. The release build may very well act as you expect, never using dest but that doesn't make the input data valid.

The debug build of STL is trying to help you by saying, "your input is bad". While the bad input might not be a problem in some circumstances, the validator can't know under what conditions you are passing it bad data. Personally, I would much rather have VS tell me about bad input in debug builds than have a runtime exception thrown in production.

Sure, you might do something like this:

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

But the validator doesn't know that so it assumes the worst, especially since fixing it is very easy for you (just always pass in a valid output iterator) and not warning you about it could have terrible consequences on your application at runtime in production.

查看更多
霸刀☆藐视天下
3楼-- · 2019-06-16 00:15

Not only does the Requires: clause need to be satisfied, but everything in the Effects: and Returns: clause needs to be satisfied as well. Let's go through them:

Effects: Copies elements in the range [first,last) into the range [result,result + (last - first)) starting from first and proceeding to last.

As first == last, then the range [result, result + 0) must be a valid range.

[iterator.requirements.general]/p7 states:

A range [i,i) is an empty range; ... Range [i,j) is valid if and only if j is reachable from i.

And p6 of the same section states:

An iterator j is called reachable from an iterator i if and only if there is a finite sequence of applications of the expression ++i that makes i == j.

From these paragraphs I conclude that given:

int* dest = nullptr;

Then [dest, dest) forms a valid empty range. So the first sentence in the Effects: paragraph looks ok to me:

For each non-negative integer n < (last - first), performs *(result + n) = *(first + n).

There are no non-negative integers n < 0, and so no assignments can be performed. So the second sentence does not prohibit dest == nullptr.

Returns: result + (last - first).

[expr.add]/p8 specifically allows one to add 0 to any pointer value and the result compares equal to the original pointer value. Therefore dest + 0 is a valid expression equal to nullptr. No problems with the Returns: clause.

Requires: result shall not be in the range [first,last).

I see no reasonable way to interpret that dest would be "in" an empty range.

Complexity: Exactly last - first assignments.

This confirms that no assignments can be done.

I can find no statement in the standard that makes this example anything but well-formed.

查看更多
贼婆χ
4楼-- · 2019-06-16 00:31

This answer is derived from the comment by @Philipp Lenk. If he supplies an answer and you find it acceptable, choose his over mine and please up-vote his original comment.

§25.1.5: Throughout this Clause, the names of template parameters are used to express type requirement [...] if an algorithm’s template parameter is OutputIterator, OutputIterator1, or OutputIterator2, the actual template argument shall satisfy the requirements of an output iterator.

§24.2.4: A class or pointer type X satisfies the requirements of an output iterator if X satisfies the Iterator requirements and the expressions in Table 108 are valid and have the indicated semantics. First line of the table: *r = o with the remark that post: r is incrementable.

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

The above code is not valid. You cannot assign to *dest in this case, therefore per the standard you cannot pass a nullptr in for the dest.

查看更多
登录 后发表回答