Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   C++ (http://www.velocityreviews.com/forums/f39-c.html)
-   -   Moving elements out of an initializer-list (http://www.velocityreviews.com/forums/t733429-moving-elements-out-of-an-initializer-list.html)

Johannes Schaub (litb) 09-16-2010 12:52 PM

Moving elements out of an initializer-list
 
It seems that the draft C++0x allows a container to act in the following
way:

template<typename T>
struct container {
/* t's backing array is a T[]. Wrap into move iterators to
* make use of move-construction. */
container(initializer_list<T> t)
:v(make_move_iterator((T*)t.begin()),
make_move_iterator((T*)t.end()))
{

}

std::vector<T> v;
};

Notice that i casted the return tpe of t.begin() and t.end() from "T const*"
to "T*", but that seems to be valid. Any comment?

Victor Bazarov 09-16-2010 02:44 PM

Re: Moving elements out of an initializer-list
 
On 9/16/2010 8:52 AM, Johannes Schaub (litb) wrote:
> It seems that the draft C++0x allows a container to act in the following
> way:
>
> template<typename T>
> struct container {
> /* t's backing array is a T[]. Wrap into move iterators to
> * make use of move-construction. */
> container(initializer_list<T> t)
> :v(make_move_iterator((T*)t.begin()),
> make_move_iterator((T*)t.end()))
> {
>
> }
>
> std::vector<T> v;
> };
>
> Notice that i casted the return tpe of t.begin() and t.end() from "T const*"
> to "T*", but that seems to be valid. Any comment?


Could you elaborate on why do you need to cast? Thanks!

V
--
I do not respond to top-posted replies, please don't ask

Johannes Schaub (litb) 09-16-2010 02:59 PM

Re: Moving elements out of an initializer-list
 
Victor Bazarov wrote:

> On 9/16/2010 8:52 AM, Johannes Schaub (litb) wrote:
>> It seems that the draft C++0x allows a container to act in the following
>> way:
>>
>> template<typename T>
>> struct container {
>> /* t's backing array is a T[]. Wrap into move iterators to
>> * make use of move-construction. */
>> container(initializer_list<T> t)
>> :v(make_move_iterator((T*)t.begin()),
>> make_move_iterator((T*)t.end()))
>> {
>>
>> }
>>
>> std::vector<T> v;
>> };
>>
>> Notice that i casted the return tpe of t.begin() and t.end() from "T
>> const*" to "T*", but that seems to be valid. Any comment?

>
> Could you elaborate on why do you need to cast? Thanks!
>


The above class can be used to do the following

container<T> c{ ifstream("file1.txt"), ifstream("file2.txt") };

And then you have "c.v" a vector of size 2 with those ifstream elements. A
move_iterator returns "T&&" from its "operator*" such that when it iterates
over a "ifstream"-sequence it returns "ifstream&&" which can be moved from.
If I wouldn't have casted, it would return "ifstream const&&", which cannot
be moved from (it's const!).

But the following does *not* work

vector<ifstream> v{ ifstream("file1.txt"), ifstream("file2.txt") };

Because the const is not casted away, this forces vector to try and copy the
elements, but streams are non-copyable. I think this either shows

- A problem with the wording of initializer_list creation (the backing-up
array is non-const but is inteded to be const?)
- Missing wording that you are not allowed to modify the backing array of an
initializer_list (effectively forbidding moving things out of it)
- Something we can apply to Standard containers to make them more widely
usable with initializer lists.
- A poorly designed initializer_list interface: Why are you allowed to move
things out, but you would need an explicit cast? Either disallow it or
explicitly allow it and make begin/end return "T*".

This is my personal opinion, of course. Please let me hear what you think
about it.

Victor Bazarov 09-16-2010 03:33 PM

Re: Moving elements out of an initializer-list
 
On 9/16/2010 10:59 AM, Johannes Schaub (litb) wrote:
> Victor Bazarov wrote:
>
>> On 9/16/2010 8:52 AM, Johannes Schaub (litb) wrote:
>>> It seems that the draft C++0x allows a container to act in the following
>>> way:
>>>
>>> template<typename T>
>>> struct container {
>>> /* t's backing array is a T[]. Wrap into move iterators to
>>> * make use of move-construction. */
>>> container(initializer_list<T> t)
>>> :v(make_move_iterator((T*)t.begin()),
>>> make_move_iterator((T*)t.end()))
>>> {
>>>
>>> }
>>>
>>> std::vector<T> v;
>>> };
>>>
>>> Notice that i casted the return tpe of t.begin() and t.end() from "T
>>> const*" to "T*", but that seems to be valid. Any comment?

>>
>> Could you elaborate on why do you need to cast? Thanks!
>>

>
> The above class can be used to do the following
>
> container<T> c{ ifstream("file1.txt"), ifstream("file2.txt") };
>
> And then you have "c.v" a vector of size 2 with those ifstream elements. A
> move_iterator returns "T&&" from its "operator*" such that when it iterates
> over a "ifstream"-sequence it returns "ifstream&&" which can be moved from.
> If I wouldn't have casted, it would return "ifstream const&&", which cannot
> be moved from (it's const!).


OK, hold on. The wording of [dcl.init.list/4] is:
"An object of type std::initializer_list<E> is constructed from an
initializer list as if the implementation
allocated an array of N elements of type E, where N is the number of
elements in the initializer list.
Each element of that array is copy-initialized with the corresponding
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
element of the initializer list, .. "

Now, how do you suppose the elements are copy-initialized if the streams
cannot be copied? Have you tried your code anywhere or is it just your
speculation?

>
> But the following does *not* work
>
> vector<ifstream> v{ ifstream("file1.txt"), ifstream("file2.txt") };
>
> Because the const is not casted away, this forces vector to try and copy the
> elements, but streams are non-copyable. I think this either shows
>
> - A problem with the wording of initializer_list creation (the backing-up
> array is non-const but is inteded to be const?)
> - Missing wording that you are not allowed to modify the backing array of an
> initializer_list (effectively forbidding moving things out of it)
> - Something we can apply to Standard containers to make them more widely
> usable with initializer lists.
> - A poorly designed initializer_list interface: Why are you allowed to move
> things out, but you would need an explicit cast? Either disallow it or
> explicitly allow it and make begin/end return "T*".
>
> This is my personal opinion, of course. Please let me hear what you think
> about it.


V
--
I do not respond to top-posted replies, please don't ask

Niels Dekker - no reply address 09-16-2010 03:41 PM

Re: Moving elements out of an initializer-list
 
Victor Bazarov wrote:
> OK, hold on. The wording of [dcl.init.list/4] is:
> "An object of type std::initializer_list<E> is constructed from an
> initializer list as if the implementation
> allocated an array of N elements of type E, where N is the number of
> elements in the initializer list.
> Each element of that array is copy-initialized with the corresponding
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> element of the initializer list, .. "
>
> Now, how do you suppose the elements are copy-initialized if the
> streams cannot be copied? Have you tried your code anywhere or is it
> just your speculation?



FWIW, I certainly would appreciate move semantics for initializer_list.
IIUC, it's intended to be reconsidered /after/ C++0x. Rodrigo Castro Campos
wrote a proposal on the issue, "Initializer lists and move semantics",
http://www.open-std.org/jtc1/sc22/wg...2008/n2801.pdf

My two cents, Niels
--
Niels Dekker
http://www.xs4all.nl/~nd/dekkerware
Scientific programmer at LKEB, Leiden University Medical Center





Johannes Schaub (litb) 09-16-2010 04:11 PM

Re: Moving elements out of an initializer-list
 
Victor Bazarov wrote:

> On 9/16/2010 10:59 AM, Johannes Schaub (litb) wrote:
>> Victor Bazarov wrote:
>>
>>> On 9/16/2010 8:52 AM, Johannes Schaub (litb) wrote:
>>>> It seems that the draft C++0x allows a container to act in the
>>>> following way:
>>>>
>>>> template<typename T>
>>>> struct container {
>>>> /* t's backing array is a T[]. Wrap into move iterators to
>>>> * make use of move-construction. */
>>>> container(initializer_list<T> t)
>>>> :v(make_move_iterator((T*)t.begin()),
>>>> make_move_iterator((T*)t.end()))
>>>> {
>>>>
>>>> }
>>>>
>>>> std::vector<T> v;
>>>> };
>>>>
>>>> Notice that i casted the return tpe of t.begin() and t.end() from "T
>>>> const*" to "T*", but that seems to be valid. Any comment?
>>>
>>> Could you elaborate on why do you need to cast? Thanks!
>>>

>>
>> The above class can be used to do the following
>>
>> container<T> c{ ifstream("file1.txt"), ifstream("file2.txt") };
>>
>> And then you have "c.v" a vector of size 2 with those ifstream elements.
>> A move_iterator returns "T&&" from its "operator*" such that when it
>> iterates over a "ifstream"-sequence it returns "ifstream&&" which can be
>> moved from. If I wouldn't have casted, it would return "ifstream
>> const&&", which cannot be moved from (it's const!).

>
> OK, hold on. The wording of [dcl.init.list/4] is:
> "An object of type std::initializer_list<E> is constructed from an
> initializer list as if the implementation
> allocated an array of N elements of type E, where N is the number of
> elements in the initializer list.
> Each element of that array is copy-initialized with the corresponding
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> element of the initializer list, .. "
>
> Now, how do you suppose the elements are copy-initialized if the streams
> cannot be copied? Have you tried your code anywhere or is it just your
> speculation?
>


I don't see why you think I would speculate. I already stated that my post
is based on the C++0x draft. This is a copy-initialization:

ifstream a = ifstream();

And it will move the ifstream since it's an rvalue and "a" has a move
constructor.


SG 09-16-2010 05:07 PM

Re: Moving elements out of an initializer-list
 
On 16 Sep., 16:59, Johannes Schaub wrote:

> I think this either shows
>
> - A problem with the wording of initializer_list creation (the
> backing-up array is non-const but is inteded to be const?)


I *do* think it's intended to be const. Section 8.5.4 paragraph 5
specifically allows a compiler to allocate/initialize these objects
statically like string literals (if possible). So, casting away
constness and modifying the objects is a bad idea.

> - Missing wording that you are not allowed to modify the backing
> array of an initializer_list (effectively forbidding moving
> things out of it)
> - Something we can apply to Standard containers to make them more
> widely usable with initializer lists.
> - A poorly designed initializer_list interface: Why are you allowed
> to move things out, but you would need an explicit cast?


What makes you think you are allowed to do that? Just because
paragraph 4 contains the following example?

" struct X {
X(std::initializer_list<double> v);
};
X x{ 1,2,3 };

The initialization will be implemented in a way roughly
equivalent to

double __a[3] = {double{1},double{2},double{3}};
X x (std::initializer_list<double>(__a,__a+3));

assuming the implementation can construct an initializer_list
with a pair of pointers. "

I don't think this proves that moving is allowed, especially because
this is just an explanation for an example with the wording "roughly
equivalent" in it.


> This is my personal opinion, of course. Please let me hear what you think
> about it. *


To be honest, I don't like std::initializer_list. In my opinion, it's
primarily for toy examples. "Look, Ma! This is how I can initialize my
vector!" In practice, one usually doesn't fill standard containers
like this. Data comes from user input, files, data bases, network
connections, etc. And for simple lookup tables we can still use
statically initialized C arrays. std::initializer_list also doesn't
play nice with move semantics. This is because std::initializer_list
has reference semantics (it doesn't really own its elements) and also
because the draft specifically allows the compiler to use read-only
memory for the elements if it sees fit. No guarantees are made as to
when the elements really have automatic storage and when not.

Victor Bazarov wrote:
> Each element of that array is copy-initialized with the
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> corresponding element of the initializer list, .. "


I don't think that "copy-initialization" rules out move construction.
But I admit that this term is confusing.

Cheers!
SG

Johannes Schaub (litb) 09-16-2010 05:21 PM

Re: Moving elements out of an initializer-list
 
SG wrote:

> On 16 Sep., 16:59, Johannes Schaub wrote:
>
>> I think this either shows
>>
>> - A problem with the wording of initializer_list creation (the
>> backing-up array is non-const but is inteded to be const?)

>
> I *do* think it's intended to be const. Section 8.5.4 paragraph 5
> specifically allows a compiler to allocate/initialize these objects
> statically like string literals (if possible). So, casting away
> constness and modifying the objects is a bad idea.
>


But it also says "if an explicit array with the same initializer
could be so allocated.". It seems to me this is only valid for a const T
without mutable members, for which the shown "container" template will not
move (because it will cast from "T const*" to "T const*").

>> - Missing wording that you are not allowed to modify the backing
>> array of an initializer_list (effectively forbidding moving
>> things out of it)
>> - Something we can apply to Standard containers to make them more
>> widely usable with initializer lists.
>> - A poorly designed initializer_list interface: Why are you allowed
>> to move things out, but you would need an explicit cast?

>
> What makes you think you are allowed to do that?


Because I have an array of non-const "T" and the initializer_list is said to
refer to that array. I am allowed to modify them. Unless the Standard
explicitly forbids it.


>> This is my personal opinion, of course. Please let me hear what you think
>> about it.

>
> To be honest, I don't like std::initializer_list. In my opinion, it's
> primarily for toy examples. "Look, Ma! This is how I can initialize my
> vector!" In practice, one usually doesn't fill standard containers
> like this. Data comes from user input, files, data bases, network
> connections, etc. And for simple lookup tables we can still use
> statically initialized C arrays. std::initializer_list also doesn't
> play nice with move semantics. This is because std::initializer_list
> has reference semantics (it doesn't really own its elements) and also
> because the draft specifically allows the compiler to use read-only
> memory for the elements if it sees fit. No guarantees are made as to
> when the elements really have automatic storage and when not.
>


I will agree to the "I don't like std::initializer_list". :)

Victor Bazarov 09-16-2010 06:15 PM

Re: Moving elements out of an initializer-list
 
On 9/16/2010 1:07 PM, SG wrote:
> Victor Bazarov wrote:
>> Each element of that array is copy-initialized with the
>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>> corresponding element of the initializer list, .. "

>
> I don't think that "copy-initialization" rules out move construction.
> But I admit that this term is confusing.


No, I didn't mean that. I meant that if copy-initialization is allowed
for creating that array behind the initializer_list<> *and* it is
possible with streams (which aren't copy-initializable, as I understand
it), then there should be no need to const-cast. Otherwise, if the
streams aren't copy-initializable, then their use thus is precluded in
an initializer_list *as well*.

V
--
I do not respond to top-posted replies, please don't ask

Johannes Schaub (litb) 09-16-2010 06:47 PM

Re: Moving elements out of an initializer-list
 
Victor Bazarov wrote:

> On 9/16/2010 1:07 PM, SG wrote:
>> Victor Bazarov wrote:
>>> Each element of that array is copy-initialized with the
>>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>>> corresponding element of the initializer list, .. "

>>
>> I don't think that "copy-initialization" rules out move construction.
>> But I admit that this term is confusing.

>
> No, I didn't mean that. I meant that if copy-initialization is allowed
> for creating that array behind the initializer_list<> *and* it is
> possible with streams (which aren't copy-initializable, as I understand
> it), then there should be no need to const-cast. Otherwise, if the
> streams aren't copy-initializable, then their use thus is precluded in
> an initializer_list *as well*.
>

They are not "CopyConstructible", using the definition of that requirement
in the Standard, because you cannot direct-initialize a stream from another
const stream:

const ifstream a("file.txt");
(ifstream(a)); // not CopyConstructible

What I meant with my collegial meaning of "copyable" was "CopyConstructible
+ Assignable", which ifstream certainly isn't. A class object is
conceptionally said to be copied by copy-assignment operator and copy-
constructors. Thus the word "copy" does in general not include moving. But
"copy" != "copy-initialize".

To "copy-initialize" just means that you initialize an object using certain
semantics - of which the most important is that only non-explicit
constructors are allowed/considered (for list-initialization, they are
considered but not allowed. For others they are not considered in the first
place). Copy-initialization semantics apply for parameter passing, return-
values and "= foo" initialization. It does not imply that only copy
constructors are allowed to be used, despite the name.

Please consult 8.5 about what "copy-initialization" means.


All times are GMT. The time now is 01:16 PM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.