On Jul 8, 2012, at 1:22 PM, Ville Voutilainen wrote:
> On 8 July 2012 20:04, Howard Hinnant <
howard....@gmail.com> wrote:
>> On Jul 8, 2012, at 1:00 PM, Nevin Liber wrote:
>>> On 8 July 2012 04:23, Nikolay Ivchenkov <
ts...@mail.ru> wrote:
>>>> On Sunday, July 8, 2012 1:55:23 AM UTC+4, Howard Hinnant wrote:
>>>>> I've requested the LWG re-open LWG 760 and address it. I've also
>>>>> suggested that your question be added to the issue.
>>>> Yes, I saw c++std-lib-32772. IMHO, it's too late to introduce new
>>>> constraints now.
>>> +1
>>> I find this pattern of looking to change the library in ways that
>>> silently break user code to be disturbing.
>> Does there exist an implementation which does something different than libc++ or gcc? If not, then what user code will be broken if we standardize existing behavior?
>> What does VC++XX do?
>
> Excuse me, but where's the rvalue reference that
>
http://cplusplus.github.com/LWG/lwg-closed.html#760 speaks
> of?
[container.requirements]/12 was existing wording when this proposed wording was drafted. It appears in the issue only to say where the proposed wording should go. The intent of what was [container.requirements]/12 now lives at [res.on.arguments]/p1/b3.
> The example in the beginning of the thread uses .back(), which
> returns an lvalue reference, not an rvalue reference.
> To me, it would make sense for that example to work. I don't see how
> it could work for non-copyable/movable types,
> because it certainly seems to try and invoke copy construction, since
> it provides a constructor argument that's
> an lvalue reference to an object of the type being constructed.
>
> I guess I can stomach a "with emplace, be careful not to emplace from
> container elements", but the example
> seems quite innocent to me, and it's a bit hard to explain exactly why
> the container gets messed up, since
> there shouldn't be any moving going on.
There is precedent for the "be careful" spec: Table 100, a.insert(p,i,j):
pre: i and j are not iterators into a.
Further info: libc++ vector::insert makes this case work and *not* by making a temporary copy (which has unknown expense). Instead it detects self referencing and recomputes the reference when detected:
template <class _Tp, class _Allocator>
typename vector<_Tp, _Allocator>::iterator
vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x)
{
pointer __p = this->__begin_ + (__position - begin());
if (this->__end_ < this->__end_cap())
{
if (__p == this->__end_)
{
__alloc_traits::construct(this->__alloc(),
_VSTD::__to_raw_pointer(this->__end_), __x);
++this->__end_;
}
else
{
__move_range(__p, this->__end_, __p + 1);
const_pointer __xr = pointer_traits<const_pointer>::pointer_to(__x);
if (__p <= __xr && __xr < this->__end_)
++__xr; // recompute reference
*__p = *__xr;
}
}
else
{
allocator_type& __a = this->__alloc();
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), __p - this->__begin_, __a);
__v.push_back(__x);
__p = __swap_out_circular_buffer(__v, __p);
}
return __make_iter(__p);
}
I believe this technique to be superior to that of making a local temporary (not for vector<int>, but for vector<expensive-to-copy>). In general, libc++ goes to extra effort to avoid gratuitous copies and even moves.
If the LWG clarifies this case such that it should work as Nikolay prefers, I would like to be able to implement it by forwarding this specific case to vector::insert with a compile-time switch.
template <class _Tp, class _Allocator>
template <class... _Args>
inline
typename vector<_Tp, _Allocator>::iterator
vector<_Tp, _Allocator>::emplace(const_iterator __position, value_type& __x)
{
return insert(__position, __x);
}
template <class _Tp, class _Allocator>
template <class... _Args>
inline
typename vector<_Tp, _Allocator>::iterator
vector<_Tp, _Allocator>::emplace(const_iterator __position, const value_type& __x)
{
return insert(__position, __x);
}
Such a solution will not address all of the cases LWG 760 refers to. But it will only penalize those clients who should be using vector::insert anyway.
Anyone have the behavior of VC++ for Nikolay's example?
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v;
v.reserve(4);
v = { 1, 2, 3 };
v.emplace(v.begin(), v.back());
for (int x : v)
std::cout << x << std::endl;
}
Howard