c++11 - Avoiding self assignment in std::shuffle -
i stumbled upon following problem when using checked implementation of glibcxx:
/usr/include/c++/4.8.2/debug/vector:159:error: attempt self move assign. objects involved in operation: sequence "this" @ 0x0x1b3f088 { type = nst7__debug6vectoriisaiieee; }
which have reduced minimal example:
#include <vector> #include <random> #include <algorithm> struct type { std::vector<int> ints; }; int main() { std::vector<type> intvectors = {{{1}}, {{1, 2}}}; std::shuffle(intvectors.begin(), intvectors.end(), std::mt19937()); }
tracing problem found shuffle
wants std::swap
element itself. type
user defined , no specialization std::swap
has been given it, default 1 used creates temporary , uses operator=(&&)
transfer values:
_tp __tmp = _glibcxx_move(__a); __a = _glibcxx_move(__b); __b = _glibcxx_move(__tmp);
as type
not explicitly give operator=(&&)
default implemented "recursively" applying same operation on members.
the problem occurs on line 2 of swap code __a
, __b
point same object results in effect in code __a.operator=(std::move(__a))
triggers error in checked implementation of vector::operator=(&&)
.
my question is: who's fault this?
- is mine, because should provide implementation
swap
makes "self swap"nop
? - is
std::shuffle
's, because should not try swap element itself? - is checked implementation's, because self-move-assigment fine?
- everything correct, checked implementation doing me favor in doing check (but how turn off)?
i have read shuffle requiring iterators valueswappable. extend self-swap (which mere runtime problem , can not enforced compile-time concept checks)?
addendum
to trigger error more directly 1 use:
#include <vector> int main() { std::vector<int> vectorofints; vectorofints = std::move(vectorofints); }
of course quite obvious (why move vector itself?). if swapping std::vector
s directly error not occur because of vector class having custom implementation of swap function not use operator=(&&)
.
it bug in gcc's checked implementation. according the c++11 standard, swappable requirements include (emphasis mine):
17.6.3.2 §4 rvalue or lvalue
t
swappable if , ift
swappable any rvalue or lvalue, respectively, of typet
any rvalue or lvalue includes, definition, t
itself, therefore swappable swap(t,t)
must legal. @ same time default swap
implementation requires following
20.2.2 §2 requires: type
t
shall moveconstructible (table 20) , moveassignable (table 22).
therefore, swappable under definition of default swap operator self-move assignment must valid , have postcondition after self assignment t
equivalent it's old value (not no-op though!) per table 22.
although object swapping not standard type, moveassignable has no precondition rv
, t
refer different objects, , long members moveassignable (as std::vector
should be) generate move assignment operator must correct (as performs memberwise move assignment per 12.8 §29). furthermore, although note states rv
has valid unspecified state, state except being equivalent it's original value incorrect self assignment, otherwise postcondition violated.
Comments
Post a Comment