Moving elements out of an initializer-list

Discussion in 'C++' started by Johannes Schaub (litb), Sep 16, 2010.

  1. 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?
     
    Johannes Schaub (litb), Sep 16, 2010
    #1
    1. Advertising

  2. 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
     
    Victor Bazarov, Sep 16, 2010
    #2
    1. Advertising

  3. 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.
     
    Johannes Schaub (litb), Sep 16, 2010
    #3
  4. 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
     
    Victor Bazarov, Sep 16, 2010
    #4
  5. 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/wg21/docs/papers/2008/n2801.pdf

    My two cents, Niels
    --
    Niels Dekker
    http://www.xs4all.nl/~nd/dekkerware
    Scientific programmer at LKEB, Leiden University Medical Center
     
    Niels Dekker - no reply address, Sep 16, 2010
    #5
  6. 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.
     
    Johannes Schaub (litb), Sep 16, 2010
    #6
  7. Johannes Schaub (litb)

    SG Guest

    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
     
    SG, Sep 16, 2010
    #7
  8. 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". :)
     
    Johannes Schaub (litb), Sep 16, 2010
    #8
  9. 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
     
    Victor Bazarov, Sep 16, 2010
    #9
  10. 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.
     
    Johannes Schaub (litb), Sep 16, 2010
    #10
  11. Johannes Schaub (litb)

    SG Guest

    On 16 Sep., 20:15, 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),


    But their move constructors are implicit. That makes them "copy-
    initializable" given rvalue initializers, doesn't it? Are we talking
    about the same thing? I'm still not sure whether you confuse "copy-
    initialization" with "copy-construction" or not.

    > then there should be no need to const-cast.


    Is is possible that you're not familiar with the std::initializer_list
    specification? Last time I checked, std::initializer_list<T>::begin/
    end both yielded pointers of type const T*.

    Cheers!
    SG
     
    SG, Sep 16, 2010
    #11
  12. On 9/16/2010 2:47 PM, Johannes Schaub (litb) wrote:
    > 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.


    Huh? Are you saying that somehow, if I *delete* or *make private* my
    copy constructor, I will *still* be able to "copy-initialize" an object
    of my class, yet not _copy-construct_ it? And please limit your
    deductions to classes only, we don't care for built-in types here.

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


    I will, as soon as you point me to the relevant paragraph which makes it
    clear how "copy-initializable" is possible if the class does not fit the
    copy-constructible requirement (has no [accessible] copy-constructor).
    Thank you.

    V
    --
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Sep 16, 2010
    #12
  13. Johannes Schaub (litb)

    SG Guest

    On 16 Sep., 20:55, Victor Bazarov wrote:
    > On 9/16/2010 2:47 PM, Johannes Schaub wrote:
    >
    > > 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.

    >
    > Huh?  Are you saying that somehow, if I *delete* or *make private* my
    > copy constructor, I will *still* be able to "copy-initialize" an object
    > of my class, yet not _copy-construct_ it?  And please limit your
    > deductions to classes only, we don't care for built-in types here.


    The term "copy initialization" doesn't imply any copying (anymore).
    It's just a mode of initialization that may also lead to an actual
    move constructions or no copy/move construction at all in case they
    are elided.

    string foo ("blah"); // "direct initialization"
    string bar = "blah"; // "copy initialization"

    The big difference here is that in the latter case only implicit
    constructors are considered and the target class type needs to be copy-
    constructible OR move-constructible (or both, but this copy/move is
    usually elided). We still call this kind of initialization "copy
    initialization" even for move-only types. This is the standardeese
    terminology which is, admittedly, a bit confusing considering movable
    types.

    Cheers!
    SG
     
    SG, Sep 16, 2010
    #13
  14. On 9/16/2010 3:05 PM, SG wrote:
    > On 16 Sep., 20:55, Victor Bazarov wrote:
    >> On 9/16/2010 2:47 PM, Johannes Schaub wrote:
    >>
    >>> 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.

    >>
    >> Huh? Are you saying that somehow, if I *delete* or *make private* my
    >> copy constructor, I will *still* be able to "copy-initialize" an object
    >> of my class, yet not _copy-construct_ it? And please limit your
    >> deductions to classes only, we don't care for built-in types here.

    >
    > The term "copy initialization" doesn't imply any copying (anymore).
    > It's just a mode of initialization that may also lead to an actual
    > move constructions or no copy/move construction at all in case they
    > are elided.
    >
    > string foo ("blah"); // "direct initialization"
    > string bar = "blah"; // "copy initialization"
    >
    > The big difference here is that in the latter case only implicit
    > constructors are considered and the target class type needs to be copy-
    > constructible OR move-constructible (or both, but this copy/move is
    > usually elided). We still call this kind of initialization "copy
    > initialization" even for move-only types. This is the standardeese
    > terminology which is, admittedly, a bit confusing considering movable
    > types.


    Can you copy-initialize a stream? Is this valid code:

    std::ifstream in = "filename.ext";

    ?

    V
    --
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Sep 16, 2010
    #14
  15. On 9/16/2010 2:55 PM, SG wrote:
    > On 16 Sep., 20:15, 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),

    >
    > But their move constructors are implicit. That makes them "copy-
    > initializable" given rvalue initializers, doesn't it? Are we talking
    > about the same thing? I'm still not sure whether you confuse "copy-
    > initialization" with "copy-construction" or not.
    >
    >> then there should be no need to const-cast.

    >
    > Is is possible that you're not familiar with the std::initializer_list
    > specification? Last time I checked, std::initializer_list<T>::begin/
    > end both yielded pointers of type const T*.


    Yes, that's my mistake. Since the object is const, one would need to
    const_cast it, I suppose. But this is (a) unclean to begin with, and
    (b) not necessarily based on a working premise (I am still unsure that
    there can be an array of streams).

    V
    --
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Sep 16, 2010
    #15
  16. * SG, on 16.09.2010 20:55:
    > On 16 Sep., 20:15, 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),

    >
    > But their move constructors are implicit. That makes them "copy-
    > initializable" given rvalue initializers, doesn't it? Are we talking
    > about the same thing? I'm still not sure whether you confuse "copy-
    > initialization" with "copy-construction" or not.


    Could you quote definitions, please.


    Cheers,

    - Alf

    --
    blog at <url: http://alfps.wordpress.com>
     
    Alf P. Steinbach /Usenet, Sep 16, 2010
    #16
  17. Victor Bazarov wrote:

    > On 9/16/2010 3:05 PM, SG wrote:
    >> On 16 Sep., 20:55, Victor Bazarov wrote:
    >>> On 9/16/2010 2:47 PM, Johannes Schaub wrote:
    >>>
    >>>> 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.
    >>>
    >>> Huh? Are you saying that somehow, if I *delete* or *make private* my
    >>> copy constructor, I will *still* be able to "copy-initialize" an object
    >>> of my class, yet not _copy-construct_ it? And please limit your
    >>> deductions to classes only, we don't care for built-in types here.

    >>
    >> The term "copy initialization" doesn't imply any copying (anymore).
    >> It's just a mode of initialization that may also lead to an actual
    >> move constructions or no copy/move construction at all in case they
    >> are elided.
    >>
    >> string foo ("blah"); // "direct initialization"
    >> string bar = "blah"; // "copy initialization"
    >>
    >> The big difference here is that in the latter case only implicit
    >> constructors are considered and the target class type needs to be copy-
    >> constructible OR move-constructible (or both, but this copy/move is
    >> usually elided). We still call this kind of initialization "copy
    >> initialization" even for move-only types. This is the standardeese
    >> terminology which is, admittedly, a bit confusing considering movable
    >> types.

    >
    > Can you copy-initialize a stream? Is this valid code:
    >
    > std::ifstream in = "filename.ext";
    >


    It's not valid in C++03, but it is valid in C++0x because streams are
    movable: The thing that happens in C++0x and C++03 is that first the char
    array is implicitly converted to "ifstream" and yields an ifstream
    temporary. Then the "in" object is direct-initialized by that temporary.

    For a copy-initialization example that does, in fact, not copy anything:

    int n = 0;
    int &rn = n; // int reference is copy-initialized.
     
    Johannes Schaub (litb), Sep 16, 2010
    #17
  18. On 9/16/2010 4:04 PM, Johannes Schaub (litb) wrote:
    > Victor Bazarov wrote:
    >
    >> On 9/16/2010 3:05 PM, SG wrote:
    >>> On 16 Sep., 20:55, Victor Bazarov wrote:
    >>>> On 9/16/2010 2:47 PM, Johannes Schaub wrote:
    >>>>
    >>>>> 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.
    >>>>
    >>>> Huh? Are you saying that somehow, if I *delete* or *make private* my
    >>>> copy constructor, I will *still* be able to "copy-initialize" an object
    >>>> of my class, yet not _copy-construct_ it? And please limit your
    >>>> deductions to classes only, we don't care for built-in types here.
    >>>
    >>> The term "copy initialization" doesn't imply any copying (anymore).
    >>> It's just a mode of initialization that may also lead to an actual
    >>> move constructions or no copy/move construction at all in case they
    >>> are elided.
    >>>
    >>> string foo ("blah"); // "direct initialization"
    >>> string bar = "blah"; // "copy initialization"
    >>>
    >>> The big difference here is that in the latter case only implicit
    >>> constructors are considered and the target class type needs to be copy-
    >>> constructible OR move-constructible (or both, but this copy/move is
    >>> usually elided). We still call this kind of initialization "copy
    >>> initialization" even for move-only types. This is the standardeese
    >>> terminology which is, admittedly, a bit confusing considering movable
    >>> types.

    >>
    >> Can you copy-initialize a stream? Is this valid code:
    >>
    >> std::ifstream in = "filename.ext";
    >>

    >
    > It's not valid in C++03, but it is valid in C++0x because streams are
    > movable: The thing that happens in C++0x and C++03 is that first the char
    > array is implicitly converted to "ifstream" and yields an ifstream
    > temporary. Then the "in" object is direct-initialized by that temporary.


    OK, so the limitation that you run into is that 'begin' returns a
    pointer to const (instead of some kind of special iterator). It would
    be nice if it returned something that would have a dereference operator
    returning an r-value reference, yes?

    > For a copy-initialization example that does, in fact, not copy anything:
    >
    > int n = 0;
    > int&rn = n; // int reference is copy-initialized.
    >


    Let's overlook the fact that it has nothing to do with
    std::initializer_list<std::ifstream>, which makes an array of objects,
    not references. So, you are suggesting that the 'initializer_list' has
    the underlying array in which move-construction is used (not
    copy-construction) for the copy-initialization of elements, yes?

    V
    --
    I do not respond to top-posted replies, please don't ask
     
    Victor Bazarov, Sep 16, 2010
    #18
  19. Johannes Schaub (litb)

    SG Guest

    On 16 Sep., 21:22, Victor Bazarov wrote:
    >
    > Can you copy-initialize a stream?


    In C++98: No.
    In C++0x: Yes.

    > Is this valid code:
    >
    >      std::ifstream in = "filename.ext";


    Is std::ifstream's constructor that takes a const char* implicit?
    The answer to both questions is "No."

    Another example regarding your first question:

    std::ifstream in1 ("filename.ext");
    std::ifstream in2 = std::move(in1); // <-- !!!

    The last line is a "copy initialization". But that doesn't mean stream
    in1 is copied to in2. In this case, it is moved. We still call this
    "copy initialization".

    Alf wrote:
    > Could you quote definitions, please.


    *sigh*

    8.5/14:
    "The initialization that occurs in the form

    T x = a;

    as well as argument passing, function return, throwing an
    exception, handling an exception and aggregate member
    initializations is called copy-initialization.
    [Note: copy-initialization may invoke a move -- end note]"


    Cheers!
    SG
     
    SG, Sep 16, 2010
    #19
  20. Victor Bazarov wrote:

    > On 9/16/2010 4:04 PM, Johannes Schaub (litb) wrote:
    >> Victor Bazarov wrote:
    >>
    >>> On 9/16/2010 3:05 PM, SG wrote:
    >>>> On 16 Sep., 20:55, Victor Bazarov wrote:
    >>>>> On 9/16/2010 2:47 PM, Johannes Schaub wrote:
    >>>>>
    >>>>>> 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.
    >>>>>
    >>>>> Huh? Are you saying that somehow, if I *delete* or *make private* my
    >>>>> copy constructor, I will *still* be able to "copy-initialize" an
    >>>>> object
    >>>>> of my class, yet not _copy-construct_ it? And please limit your
    >>>>> deductions to classes only, we don't care for built-in types here.
    >>>>
    >>>> The term "copy initialization" doesn't imply any copying (anymore).
    >>>> It's just a mode of initialization that may also lead to an actual
    >>>> move constructions or no copy/move construction at all in case they
    >>>> are elided.
    >>>>
    >>>> string foo ("blah"); // "direct initialization"
    >>>> string bar = "blah"; // "copy initialization"
    >>>>
    >>>> The big difference here is that in the latter case only implicit
    >>>> constructors are considered and the target class type needs to be copy-
    >>>> constructible OR move-constructible (or both, but this copy/move is
    >>>> usually elided). We still call this kind of initialization "copy
    >>>> initialization" even for move-only types. This is the standardeese
    >>>> terminology which is, admittedly, a bit confusing considering movable
    >>>> types.
    >>>
    >>> Can you copy-initialize a stream? Is this valid code:
    >>>
    >>> std::ifstream in = "filename.ext";
    >>>

    >>
    >> It's not valid in C++03, but it is valid in C++0x because streams are
    >> movable: The thing that happens in C++0x and C++03 is that first the char
    >> array is implicitly converted to "ifstream" and yields an ifstream
    >> temporary. Then the "in" object is direct-initialized by that temporary.

    >
    > OK, so the limitation that you run into is that 'begin' returns a
    > pointer to const (instead of some kind of special iterator). It would
    > be nice if it returned something that would have a dereference operator
    > returning an r-value reference, yes?
    >


    Certainly. I thought about rvalue ref qualifiers:

    move_iterator<T*> begin() && { return __begin; }
    T const* begin() const& { return __begin; }

    But it won't work that way because it's an lvalue again when it happens to
    sit in the constructor's parameter :/

    You can overload the constructor though

    template<typename T>
    struct container {
    container(initializer_list<T>&& i) { /* move ... */ }
    container(initializer_list<T> const&i) { /* copy ... */ }
    };

    But it looks a bit weird when seen together with the copy behavior of
    initializer_list (no deep copy).


    >> For a copy-initialization example that does, in fact, not copy anything:
    >>
    >> int n = 0;
    >> int&rn = n; // int reference is copy-initialized.
    >>

    >
    > Let's overlook the fact that it has nothing to do with
    > std::initializer_list<std::ifstream>, which makes an array of objects,
    > not references. So, you are suggesting that the 'initializer_list' has
    > the underlying array in which move-construction is used (not
    > copy-construction) for the copy-initialization of elements, yes?
    >


    The initializer_list refers to it. It does not contain it. If you copy an
    initializer_list, you just copy pointers, not the real elements (see above).
    The draft guarantees that the initializer_list refers to that non-const
    array, if "T" is nonconstant (by the text you quoted earlier from 8.5.4/4).
     
    Johannes Schaub (litb), Sep 16, 2010
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. helmut januschka

    warning: excess elements in scalar initializer

    helmut januschka, Mar 6, 2004, in forum: C Programming
    Replies:
    5
    Views:
    15,377
    Joe Wright
    Mar 6, 2004
  2. Adam Hartshorne
    Replies:
    2
    Views:
    390
    Nitin Motgi
    Jan 27, 2006
  3. linkswanted
    Replies:
    0
    Views:
    1,274
    linkswanted
    Dec 21, 2007
  4. linkswanted
    Replies:
    0
    Views:
    1,605
    linkswanted
    Jan 6, 2008
  5. Costin GamenÈ›
    Replies:
    1
    Views:
    313
    Peter Otten
    Nov 9, 2010
Loading...

Share This Page