Topic: Draft specification of <valarray> makes it practically useless


Author: phalpern@truffle.ma.ultranet.com (Pablo Halpern)
Date: 1998/03/10
Raw View
Christopher Eltschka <celtschk@physik.tu-muenchen.de> wrote:

>Hyman Rosen wrote:
>>
>> Arch Robison <robison@kai.com> writes:
>> > Therein lies the rub.  Section 8.5.3 paragraph 8 (both the Final and
>> > December 1996) drafts) specifically gives the compiler permission to
>> > generate a temporary when binding a reference to an rvalue.

Note the term rvalue, here, it is very important.

>> > Section
>> > 12.2 (both drafts) say that even if the compiler optimizes away a
>> > temporary, it must enforce the access permissions that would be involved
>> > in generating the temporary.  I.e., the standard is saying that a
>> > program's correctness does not depend upon the cleverness of a
>> > particular implementation, and clever implementations must reject a
>> > program if the unclever implementation would reject it.  Thus is does
>> > matter that the function result is an rvalue.
>>
>> But Christopher was explaining that the rvalueness is irrelevant,
>> because the references are const. No correct implementation can
>> reject this construct, whether or not it chooses to copy the rvalue
>> before binding the reference.

But you (Christopher) were wrong about the rvalueness being irrelevant.
You were looking at the wrong part of the problem. (See my other post in
this thread. Briefly: The existence of the temporary/rvalue) itself is
the problem, not what you can do with the temporary/rvalue.) When
binding an rvalue to a const reference, it is often impossible to
optimize away the temporary. Therefore, the standard never *requires*
the compiler to do so, even in those cases where it *would* be possible.
>
>Well, I now looked up 8.5.3 in the CD2, and was astonished that AR is
>indeed correct: Section 8.5.3 paragraph 9 reads:

[ CD-2 quote deleted ]

Yes. He is correct.

>
>(Note that para.8 makes no difference between binding to the complete
>object or to a subobject of it)
>
>I don't understand the why of allowing this extra copying, as it
>should IMHO be fairly simple to avoid it, and indeed the most common
>reason of using const references *is* to avoid copies (think of
>f(T const&) instead of F(T)!). Now, if copying is allowed, the
>T const& version may sometimes even be *more* expensive. Example:
>
>class PlainAndSimple
>{
>  int a, b, c;
>  double d,e,f;
>public:
>  // some member functions; uses default copy
>};
>
>void ByValue(PlainAndSimple);
>
>void ByReference(PlainAndSimple const&);
>
>class HugeAndComplicated

I don't get what this has to do with the above. Did you intend to derive
HugeAndComplicated from PlainAndSimple? For the remainder of this post,
I will assume you did.

class HugeAndComplicated : public PlainAndSimple
>{
>  // complicated implementation, including much dynamic memory etc.
>public:
>  HugeAndComplicated(HugeAndComplicated const&);
>      // does a lot of stuff
>  // ...
>};
>
>HugeAndComplicated get();

Return by value. OK.
>
>int main()
>{
>  ByValue(get());
>    // copies the PlainAndSimple part (medium expensive)

Maybe. The compiler is allowed to copy the result of get() into a
HugeAndComplicated temporary and then copy the PlainAndSimple part of
the temporary into the parameter of ByValue(). Potentially two copies,
one of which is very expensive.

>  ByReference(get())
>    // may do no copy at all (cheap)
>    // may copy the HugeAndComplicated object (*very* expensive!)

I don't see how it can avoid doing a copy. *Some* temporary must be
created, whether it is of type PlainAndSimple or HugeAndComplicated.
Otherwise, what would the reference be bound to? It cannot be bound to a
local variable within get(), since the invocation of get() is long gone
by the time ByReference() gets control. Therefore, the result of get()
must be copied into the caller's scope.

Now, a temporary of type HugeAndComplicated *must* be generated unless
the compiler can prove that the parameter to ByReference is not used
polymorphically.

For example:

class PlainAndSimple
{
  public:
    virtual void func() const;
};

class HugeAndComplicated : public PlainAndSimple
{
  public:
    void func() const;
};

HugeAndComplicated get();  // return by value

ByReference(PlainAndSimple const& x)
{
  x.func();  // Actual function call depnds on *dynamic* type of x
}

int main()
{
  ByReference(get());
  //          ^^^^^  rvalue of type HugeAndComplicated
}

I believe that the draft standard requires that
HugeAndComplicated::func() get called in the above scenario. This
requires that a HugeAndComplicated reference be passed to ByReference.
This, in turn, requires that a HugeAndComplicated object exist to which
the reference is being bound. This, in turn requires that a temporary
HugeAndComplicated object be created to hold the result of get(). This,
in turn, requires that the copy constructor be accessible. I think the
(draft) standard has it right here. A compiler cannot optimise this
particular temporary away. I don't think we want go down the rat hole of
dictating which temporaries the compiler *must* optimise away.
>}
>
>Also note that especially for parameters, the compiler must be
>able to generate the direct binding variant as well, for the copy
>constructor.

This doesn't make sense to me. What variants are you talking about. A
const reference is always directly bound. The question is what is it
bound to? If it is bound to a temporary, then the temporary must exist.

>
>So I'd say not just valarray is buggy, but binding to const references
>is badly defined.

I disagree. There is no magic to binding to const references. If you
pass an lvalue to a function that takes a const or non-const reference,
then the reference is bound to the lvalue. Period. No copying is
allowed. If, however, the reference is being bound to an rvalue, then
the compiler is allowed to generate a temporary object to hold that
rvalue and the access restrictions on the copy constructor must be
enforced, whether or not the compiler is able to optimise away the
temporary. This is quite reasonable. The only "magic" is that you are
even allowed to pass an rvalue to a function that takes a reference in
the first place, since there is no obvious object for the reference to
be bound to. The language allows the object to be a temporary, thus
making lots of code compile that wouldn't otherwise. The restriction
that the reference must be const was debated and decided. There were
good arguments on both sides, but I agree with the way things turned
out.

Valarray, on the other hand *is* broken IMO, because certain copy
constructors are private, thus making return-by-value useless with those
types.

-------------------------------------------------------------
Pablo Halpern                   phalpern@truffle.ultranet.com

I am self-employed. Therefore, my opinions *do* represent
those of my employer.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1998/03/06
Raw View
Christopher Eltschka wrote:

> Well, I now looked up 8.5.3 in the CD2, and was astonished that AR is
> indeed correct: Section 8.5.3 paragraph 9 reads:
>

>                   const A& rca = f();     // Either bound directly or
>                                           //   the entire B object is
> copied and
>                                           //   the reference is bound to
> the
>                                           //   A sub-object of the copy
>        --end example]
>
> (Note that para.8 makes no difference between binding to the complete
> object or to a subobject of it)
>
> I don't understand the why of allowing this extra copying, as it
> should IMHO be fairly simple to avoid it, and indeed the most common
> reason of using const references *is* to avoid copies

Indeed, there were discutions in Morristown about that. I didn't
felt very concerned about that, and don't remember the end of the
discution, but it appears in the FDIS that nothing changed.

> So I'd say not just valarray is buggy, but binding to const references
> is badly defined.

I agree.

This is clearly a defect, and IMO must be corrected (it's
interresting to see that valarray makes this bizare rule
a defect: thanks valarray !). So valarray actually _is_
usefull.

--

Valentin Bonnard                mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Hyman Rosen <uunet!jyacc!hymie@ncar.UCAR.EDU>
Date: 1998/03/07
Raw View
Valentin Bonnard <bonnardv@pratique.fr> writes:
> Christopher Eltschka wrote:
> > So I'd say not just valarray is buggy, but binding to const references
> > is badly defined.
>
> This is clearly a defect, and IMO must be corrected

It seems to me that the standard is trying to support implementations
where a function returns its result into a statically allocated area,
whereupon the caller copies it into its ultimate destination. I think
some PC C compilers did this for floating point returns some years ago.

It breaks reentrant code, so it's not a terribly good implementation
technique, but maybe they didn't want to forbid it outright.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Arch Robison <robison@kai.com>
Date: 1998/03/04
Raw View
Christopher Eltschka wrote:

> Well, I'd read that as:
>
> v.operator[](s).operator+=(valarray<float>(v.operator[](s)));
>
> Now each of the operator[]'s return a temporary (rvalue) slice_array.
> On the left one, the member operator+= is called (as member, it may be
> called on a temporary; a copy is not required). This operator takes
> a reference to a valarray<float>, therefore a conversion is needed
> on the right slice_array (rvalue allowed, because operator+= takes a
> const
> reference). The conversion is defined as valarray constructor. As this
> constructor takes a reference, there is no need to copy the slice_array
> here (and because it's const reference, it doesn't matter that's a
> function result - that is, an rvalue - either).

Therein lies the rub.  Section 8.5.3 paragraph 8 (both the Final and
December 1996) drafts) specifically gives the compiler permission to
generate a temporary when binding a reference to an rvalue.  Section
12.2 (both drafts) say that even if the compiler optimizes away a
temporary, it must enforce the access permissions that would be involved
in generating the temporary.  I.e., the standard is saying that a
program's correctness does not depend upon the cleverness of a
particular implementation, and clever implementations must reject a
program if the unclever implementation would reject it.  Thus is does
matter that the function result is an rvalue.

Arch D. Robison       Kuck & Associates Inc.
robison@kai.com       1906 Fox Drive
217-356-2288 ext. 56      Champaign IL 61820
Lead Developer for KAI C++     http://www.kai.com/C_plus_plus/index.html
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Hyman Rosen <uunet!jyacc!hymie@ncar.UCAR.EDU>
Date: 1998/03/05
Raw View
Arch Robison <robison@kai.com> writes:
> Therein lies the rub.  Section 8.5.3 paragraph 8 (both the Final and
> December 1996) drafts) specifically gives the compiler permission to
> generate a temporary when binding a reference to an rvalue.  Section
> 12.2 (both drafts) say that even if the compiler optimizes away a
> temporary, it must enforce the access permissions that would be involved
> in generating the temporary.  I.e., the standard is saying that a
> program's correctness does not depend upon the cleverness of a
> particular implementation, and clever implementations must reject a
> program if the unclever implementation would reject it.  Thus is does
> matter that the function result is an rvalue.

But Christopher was explaining that the rvalueness is irrelevant,
because the references are const. No correct implementation can
reject this construct, whether or not it chooses to copy the rvalue
before binding the reference.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/03/06
Raw View
Hyman Rosen wrote:
>
> Arch Robison <robison@kai.com> writes:
> > Therein lies the rub.  Section 8.5.3 paragraph 8 (both the Final and
> > December 1996) drafts) specifically gives the compiler permission to
> > generate a temporary when binding a reference to an rvalue.  Section
> > 12.2 (both drafts) say that even if the compiler optimizes away a
> > temporary, it must enforce the access permissions that would be involved
> > in generating the temporary.  I.e., the standard is saying that a
> > program's correctness does not depend upon the cleverness of a
> > particular implementation, and clever implementations must reject a
> > program if the unclever implementation would reject it.  Thus is does
> > matter that the function result is an rvalue.
>
> But Christopher was explaining that the rvalueness is irrelevant,
> because the references are const. No correct implementation can
> reject this construct, whether or not it chooses to copy the rvalue
> before binding the reference.

Well, I now looked up 8.5.3 in the CD2, and was astonished that AR is
indeed correct: Section 8.5.3 paragraph 9 reads:

9     The  appropriate  copy constructor must be callable whether or not
      the copy is actually done.  [Example:
                  struct A { };
                  struct B : public A { } b;
                  extern B f();
                  const A& rca = f();     // Either bound directly or
                                          //   the entire B object is
copied and
                                          //   the reference is bound to
the
                                          //   A sub-object of the copy
       --end example]

(Note that para.8 makes no difference between binding to the complete
object or to a subobject of it)

I don't understand the why of allowing this extra copying, as it
should IMHO be fairly simple to avoid it, and indeed the most common
reason of using const references *is* to avoid copies (think of
f(T const&) instead of F(T)!). Now, if copying is allowed, the
T const& version may sometimes even be *more* expensive. Example:

class PlainAndSimple
{
  int a, b, c;
  double d,e,f;
public:
  // some member functions; uses default copy
};

void ByValue(PlainAndSimple);

void ByReference(PlainAndSimple const&);

class HugeAndComplicated
{
  // complicated implementation, including much dynamic memory etc.
public:
  HugeAndComplicated(HugeAndComplicated const&);
      // does a lot of stuff
  // ...
};

HugeAndComplicated get();

int main()
{
  ByValue(get());
    // copies the PlainAndSimple part (medium expensive)

  ByReference(get())
    // may do no copy at all (cheap)
    // may copy the HugeAndComplicated object (*very* expensive!)
}

Also note that especially for parameters, the compiler must be
able to generate the direct binding variant as well, for the copy
constructor.

So I'd say not just valarray is buggy, but binding to const references
is badly defined.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: phalpern@truffle.ma.ultranet.com (Pablo Halpern)
Date: 1998/03/06
Raw View
Christopher Eltschka <celtschk@physik.tu-muenchen.de> wrote:

>>     v[s] += v[s];
>
>Well, I'd read that as:
>
>v.operator[](s).operator+=(valarray<float>(v.operator[](s)));
>
>Now each of the operator[]'s return a temporary (rvalue) slice_array.
>On the left one, the member operator+= is called (as member, it may be
>called on a temporary; a copy is not required).

The problem is not calling operator+=() on the temporary. The problem is
*generating the temporary* in the first place. In order to create a
temporary slice_array, it is necessary to call the copy constructor for
slice_array, since valarray::operator[] returns a slice_array *by
value*. The same is true with the right-hand side. Thus, I would have to
agree with Arch that this definition is unusable. How can you use a
function that returns an rvalue of a type that has an inaccessible copy
constructor?
-------------------------------------------------------------
Pablo Halpern                   phalpern@truffle.ultranet.com

I am self-employed. Therefore, my opinions *do* represent
those of my employer.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Arch Robison <robison@kai.com>
Date: 1998/03/03
Raw View
While working on the next release of KAI C++, I noticed that
the Final International Draft Standard specification of
<valarray> specification makes the class almost useless
because slice_array and similar classes cannot be used
in the way that I presume was intended.

Here's an excerpt with all relevant particulars.
//------------------------------------------------------------------------
namespace std {
    typedef unsigned long size_t;
    template<class T> class valarray;           // 26.3.1
    class slice;
    template<class T> class slice_array;

    template<class T> class valarray {          // 26.3.2
    public:
        valarray( const slice_array<T>& );      // 26.3.2.1
        slice_array<T> operator[]( slice );     // 26.3.2.4
    };

    class slice {
    public:
        slice( size_t, size_t, size_t );
    };

    template<class T> class slice_array { // 26.3.5
    public:
        void operator+=( const valarray<T>&) const;
    private:
        slice_array( const slice_array& );      // 26.3.5
    };
}

using namespace std;    // User code

void foo( valarray<float>& v, slice& s ) { // User code
    v[s] += v[s];
}
//------------------------------------------------------------------------
The example function foo uses valarray in the way that I believe was intended,
though I can't find a single example of using slices in the entire draft.
Anyway, the expression ``v[s] += v[s]'' appears to be illegal according
to the draft, because the right-hand-side v[s] is a function result
for a class with a private copy constructor (26.3.5).  Section 12.2
paragraph 2 says that a compiler might generate a temporary copy of v[s].
But Section 12.2 paragraph 1 says the compiler must enforce accessibility
restrictions for such a temporary, even if the temporary is optimized away.

If my interpretations of Sections 26.3 or 12.2 are wrong, please clarify.
It would seem that the specifications of slice_array, gslice_array,
mask_array, and indirect_array were not tested with a compiler that
enforces Section 12.2 .

Arch D. Robison       Kuck & Associates Inc.
robison@kai.com       1906 Fox Drive
217-356-2288 ext. 56      Champaign IL 61820
Lead Developer for KAI C++     http://www.kai.com/C_plus_plus/index.html
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/03/03
Raw View
Arch Robison wrote:
>
> While working on the next release of KAI C++, I noticed that
> the Final International Draft Standard specification of
> <valarray> specification makes the class almost useless
> because slice_array and similar classes cannot be used
> in the way that I presume was intended.
>
> Here's an excerpt with all relevant particulars.
> //------------------------------------------------------------------------
> namespace std {
>     typedef unsigned long size_t;
>     template<class T> class valarray;           // 26.3.1
>     class slice;
>     template<class T> class slice_array;
>
>     template<class T> class valarray {          // 26.3.2
>     public:
>         valarray( const slice_array<T>& );      // 26.3.2.1
>         slice_array<T> operator[]( slice );     // 26.3.2.4
>     };
>
>     class slice {
>     public:
>         slice( size_t, size_t, size_t );
>     };
>
>     template<class T> class slice_array {       // 26.3.5
>     public:
>         void operator+=( const valarray<T>&) const;
>     private:
>         slice_array( const slice_array& );      // 26.3.5
>     };
> }
>
> using namespace std;                            // User code
>
> void foo( valarray<float>& v, slice& s ) {      // User code
>     v[s] += v[s];
> }
> //------------------------------------------------------------------------
> The example function foo uses valarray in the way that I believe was intended,
> though I can't find a single example of using slices in the entire draft.
> Anyway, the expression ``v[s] += v[s]'' appears to be illegal according
> to the draft, because the right-hand-side v[s] is a function result
> for a class with a private copy constructor (26.3.5).  Section 12.2
> paragraph 2 says that a compiler might generate a temporary copy of v[s].
> But Section 12.2 paragraph 1 says the compiler must enforce accessibility
> restrictions for such a temporary, even if the temporary is optimized away.
>
> If my interpretations of Sections 26.3 or 12.2 are wrong, please clarify.
> It would seem that the specifications of slice_array, gslice_array,
> mask_array, and indirect_array were not tested with a compiler that
> enforces Section 12.2 .

Well, I'd read that as:

v.operator[](s).operator+=(valarray<float>(v.operator[](s)));

Now each of the operator[]'s return a temporary (rvalue) slice_array.
On the left one, the member operator+= is called (as member, it may be
called on a temporary; a copy is not required). This operator takes
a reference to a valarray<float>, therefore a conversion is needed
on the right slice_array (rvalue allowed, because operator+= takes a
const
reference). The conversion is defined as valarray constructor. As this
constructor takes a reference, there is no need to copy the slice_array
here (and because it's const reference, it doesn't matter that's a
function result - that is, an rvalue - either). So there's nowhere
the need to copy a slice_array. (There may be the need inside
v.operator[], however, but I don't think the standard disallows
to make valarray<T> a friend of slice_array<T>.)

So if I'm not completely mistaken, there's no problem at all with this
code.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]