Topic: ofstream("temp") << foo


Author: phalpern@truffle.ma.ultranet.com (Pablo Halpern)
Date: 1997/06/23
Raw View
marcsh@corel.ca (Marc Sherman) wrote:

>In article <33a7d0b9.13033427@news.ma.ultranet.com>, phalpern@truffle.ma.ultranet.com (Pablo Halpern) wrote:
>>template <class T> inline
>>ostream& operator<< (const ostream& os, const T& t)
>
>Is this in fact legal?  ostream("file") returns a non-const ostream, so
>won't overload resolution will pick the standard operator<<(ostream&,
>const T&), and then produce an error for binding a temp to a non-const
>reference?  Or have the overload resolution rules been modified to
>prefer a const reference over a non-const reference for a temporary?

It is legal. The overloading rules select a best match out of a set of
candidates. The function that takes a non-const parameter is eliminated
from the set of candidates in this case.

-------------------------------------------------------------
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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1997/06/23
Raw View
On 08 Jun 97 07:30:30 GMT, "Bradd W. Szonye" <bradds@concentric.net>
wrote:

>One obvious disadvantage of my idea: it requires a compiler extension. One
>obvious disadvantage of the lvalue template: it "encourages" naive
>developers to slap an lvalue() around casts they can't get to work and
>don't really understand. (I'm not sure whether that's Machiavelli or Murphy
>at work.)

Rather than introduce a workaround template such as lvalue or a new
use for an existing keyword, I've wondered whether or not it's
possible to reliably distinguish intentional binding of rvalues to
non-const references from unintentional binding, so that everything
will just work as one would want it to. I expressed this in another
message:

------------

From: dHarrison@worldnet.att.net (Doug Harrison)
Newsgroups: comp.lang.c++.moderated
Subject: Re: const or not?
Date: Fri, 16 May 1997 22:07:51 GMT
Message-ID: <337dc1eb.3489637@netnews.worldnet.att.net>

On 16 May 1997 01:51:45 -0400, Oleg Zabluda <zabluda@math.psu.edu>
wrote:

>You can't bind a non-const reference ( A& ) to a temporary ( A() ).

That is indeed a language rule.

>Becuse changing a temporary just doesn't make any sense.

It's interesting/frustrating that while one can say:

 A().fun(); // fun() is a non-const member.
 A GetA() { return A(); }
 GetA().fun();

one cannot say:

 void fun(A& a) { a.fun(); }
 fun(A()); // Is an error.
 fun(GetA()); // Also an error.

In trying to protect the programmer from saying fun(a1+a2), or fun(b),
where b could be converted to an A via a conversion function, C++
makes some useful idioms involving abstract classes burdensome to
implement and explain. For example, suppose A is an abstract class,
and you want to pass objects of a derived class X to a function taking
an A&. You can't pass by value, and passing by const reference would
be misleading, not to mention that it would make writing A and its
derived classes more difficult. Now make class X a class template
whose instantiations have really long names, that you want to mitigate
by a function template that is to class X as std::back_inserter is to
std::back_insert_iterator. In order to implement this such that the
usage will be as natural as std::back_inserter, and also comply with
the DWP, you have to pass by const reference.

As I understand it, the motivation behind the prohibition against
binding rvalues to non-const references has to do with what I call
"indirect temporaries". I view the result of a1+a2 or a conversion as
an indirect temporary, while I view the results of A(), GetA(), and
even a direct call of operator+(a1,a2) as "direct temporaries". The
difference is that a direct temp is requested explicitly by the
programmer, while indirect temps are generated more indirectly and
subtly.

I've come to wish direct temporaries of class X could be bound to
function parameters of type X& or A&, where A is a base class of X.
Indeed, compilers tend to allow this, but usually with a warning, that
unfortunately doesn't distinguish between obviously intentional
binding of direct temps to non-const references and probably
accidental binding of indirect temps. I'd be interested to hear
arguments as to why direct temps shouldn't be bound as suggested
above, especially since A().fun() and GetA().fun() are allowed. (Note:
I'm not questioning the existing rules vis a vis built-in types. I
just haven't run across any compelling counterexamples demonstrating
the existing rules are appropriate for direct temporaries of class
type.)

------------

What do you think? Is the above workable? Do you know scenarios where
it would be dangerous? I'm still interested to be convinced the
current rules help more than hinder in this context.

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Richard See <Richard.See@vega.co.uk>
Date: 1997/06/20
Raw View
Pablo Halpern wrote:
>
> The above problem can be solved in at least three ways without extending
> the language and without using John Potter's lvalue() technique.
>
> Solution 1:
>
> template <class T> inline
> ostream& operator<< (const ostream& os, const T& t)
> {
>   return const_cast<ostream&> (os) << t;
> }
>
> ostream("file") << foo;
>
(Other solutions followed).

This looks like a useful work around.  I expect I'll use it.

However, I view this as a work around to a language problem rather than
a solution to a code-writing problem.  The reasons:
1) as James Kanze points out illuminatingly, it is the class designer
(of ostream in this case) who knows whether it is meaningful to alter a
temporary object, and the meaningfullness is on a per class basis.  It
would be useful if there was someway of telling the compiler of the
designer's intention within the class.
2) I feel the asymmetry between non-const member functions which can be
called on temporaries and global functions taking non-const references
that can't take temporaries is ugly, unnecessary and (worst) a potential
cause of errors.  (see the thread "Passing temporary variables by
reference" for details).
3) Brian Parker's proxy problem isn't addressed by these work arounds.

An additional factor to consider is inheritance.  Consider:

// Example code, will not compile
// Designer of B doesn't want temporary Bs bound to non-const
references.
class B {};

// Designer of D does want temporary Ds to bind to non-const references.
class D : public B {
 // some language extension to tell the compiler what to allow.
};

void make_alteration(B&);

void f(void) {
 // the next line should not compile (IMO), as none of the useful
 // behaviour of D will be called by make_alteration.
 // (I'm assuming no down casts will be performed.  I don't think
 // that's an unfair assumption.)
 make_alteration(D());
}

This behaviour would be given by Brian Parker's suggestion of using a
cast operator (which I'd be keen on if it didn't rely on the current
inconsitency described in (2) above), but not by Bradd Szonye's mutable
constructors.  It is similar to Pablo Halpern's solution's behaviour.

My background is using C++ rather than writing compilers or working on
grammar, so I'm sure there are gaps in my knowledge.  Nevertheless, I
propose an alternative mechanism for avoiding accidental alterations of
temporaries, except for when the class designer has specified that the
alterations may be useful:

(Insert standard acknowledgement for ideas.  Mistakes are my own.)

a) Remove the rule preventing binding of rvalues to non-const
references.
b) Have the compiler treat all temporaries/rvalues as const objects even
in the absence of a const specifier...
c) ...unless the type that the temporary is being bound to is specified
as an alterable class by the designer, or a member function that was
defined in a class specified as alterable by the designer is being
called (either explictly by the coder, or implicitly by the compiler for
a cast) in which case the compiler should treat them as non-const
objects unless they are explictly created as const objects.

(Clearly anyone who explictly writes a function:
const AlterableClass foo(void);
expects a const object of type AlterableClass to be returned, even if
the designer of AlterableClass says returning a non-const object may be
meaningful sometimes.)

(Note it is the type of the variable to which the temporary is being
bound, not the type of the temporary, which is important.  This is
because of the inheritance issues mentioned above.)

A language addition would be necessary to allow the designer to inform
the compiler when a class should have temporaries created as non-const
rather than const.  I'll follow Bradd Szonye's suggestion of reusing the
mutable keyword:
mutable class M {};
seems the most readable, although this may cause problems for mutable
member data of anonymous type, as
class C { mutable class {} a; };
already has a meaning.  (if it's not possible to create temporaries of
an anonymous type, then this might not be a problem).
Alternatively
class mutable M {};
or
class M mutable {};
seem adequate to me.  Besides, the syntax for this is not really the
main thing I'm trying to suggest.

As far as I can see, proposals (a) and (b) together would have the same
effect as the current rule, except that non-const member functions would
not be able to be called on temporaries.  (c) then addresses the issues
raised by Bradd Szonye and Brian Parker on a per class basis, without
needing to invoke an extra user defined conversion.

Other Issues:
i) Inheritance and accessability of class mutability.
(A different inheritance issue from the one mentioned above).
I haven't encountered enough examples of inheritance from classes which
I would expect to be mutable to be able to offer any strong opinions on
this, but I would expect that if it's reasonable to bind a temporary to
an ostream&, then it'd be reasonable to bind a temporary to an
ostringstream& too.  So I guess mutability should be inherited,
presumably qualified by the access specifier of the inheritance.  Maybe
mutability syntax should be
class C public mutable {};
to take access into account.

ii) Backwards compatibility.
(a) and (b) together would break little more code than was broken with
the introduction of the current rule, and much of this would involve
classes which had their behaviour limited by the current rule.
(c) would allow any code broken by (a) and (b) or by the current rule to
be fixed, and should not break any existing code in itself.  I would
expect that for old libraries, headers could be edited to make classes
mutable even when source code is not available.


My main reservation is complexity.  Comments?

Richard.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: marcsh@corel.ca (Marc Sherman)
Date: 1997/06/22
Raw View
In article <33a7d0b9.13033427@news.ma.ultranet.com>, phalpern@truffle.ma.ultranet.com (Pablo Halpern) wrote:
>The above problem can be solved in at least three ways without extending
>the language and without using John Potter's lvalue() technique.
>
>Solution 1:
>
>template <class T> inline
>ostream& operator<< (const ostream& os, const T& t)
>{
>  return const_cast<ostream&> (os) << t;
>}
>
>ostream("file") << foo;
>

Is this in fact legal?  ostream("file") returns a non-const ostream, so
won't overload resolution will pick the standard operator<<(ostream&,
const T&), and then produce an error for binding a temp to a non-const
reference?  Or have the overload resolution rules been modified to
prefer a const reference over a non-const reference for a temporary?

- Marc
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: bparker@gil.com.au (Brian Parker)
Date: 1997/06/15
Raw View
On 13 Jun 1997 09:23:06 PDT, "Bradd W. Szonye" <bradds@concentric.net>
wrote:
>Brian Parker <bparker@gil.com.au> wrote in article
><339fa5f8.31005447@news.ipswich.gil.com.au>...
>>
>> Where this behaviour is not desired for particular objects, const
>> temporaries could be returned which would prevent the application of
>> the (non-const) operator T&(). (in fact, if converting constructors
>> could be declared to return const temporaries, that could be used to
>> achieve the effect of the suggested mutable constructors).
>
>(Hope I haven't trimmed too much context.) Sounds like a good idea--doing
>it withing the existing language. However, unless I've misunderstood you,
>doesn't that invoke the "two user-defined conversions" problem? Or do you
>just write a function which looks like a constructor but actually isn't? Or
>is the first one considered explicit? Details/examples, if you don't mind.
>--
>Bradd W. Szonye

Good point. I was thinking only of explicit constructor and function
calls, as in the ofstream example (hence the above suggestion for
const constructors applies to all constructors, not just converting
constructors as I wrote).
However, the case of temporaries created via implicit conversions
binding to non-const references would, as you point out, still be
disallowed due to the need for two user-defined conversions. IMHO,
that is not necessarily a bad thing as in most cases one would only
want to be able to modify an explicitly created temporary.

BTW, an alternative run-time solution to allow differing behaviour
between constructors would be to test a flag in the operator T&( )
that indicates whether the temporary should be bindable to a
reference, and throw an exception otherwise. This flag could be set
accordingly in the various constructors.

In any case, I think that the most important capability is to be able
to allow an (explicitly created) class temporary to bind to a
non-const reference- changing this on a per-constructor basis is just
further fine-tuning and may not be worth the extra syntax needed.

Some examples follow-
--------------------------------------------------------
// current CD2 rules

void f(A&) {}

struct A {};
f(A( ));  // disallowed as per normal
------------------------------------------------------
// if operator T&() was able to bind rvalues

void f(B&) {}

struct B { operator B&( ) {return *this;} };
f(B( ));  // OK, by default explicit B temps now treated as
 //  lvalues

B g() {return B( );}
f(g( ));   // OK

const B h() {return B( );}
f(h( ));   // disallowed, h() treated as rvalue
-------------------------------------------------------
// if, in addition, constructors could return const values

void f(C&) {}

struct C {
 operator C&() {return *this;}
 C() {}

 const C(double) {} // non-standard syntax
}

f(C( ));  // OK, by default C temps treated as lvalues

f(C(2.0)); // disallowed as C(2.0) treated as rvalue
--------------------------------------------------------
// as an alternative to the above,
// run-time checking could be done

void f(D&) {}

struct D{
 operator D&() {
    if (is_lvalue_like) return *this;
    else throw "bad";
 }

 D( ) : is_lvalue_like(true) {}

 D(double) : is_lvalue_like(false) {}

 bool is_lvalue_like;
};

f(D( )); // OK

f(D(2.0)); // throws exception
-------------------------------------------------

,Brian Parker (bparker@gil.com.au)

---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: phalpern@truffle.ma.ultranet.com (Pablo Halpern)
Date: 1997/06/17
Raw View
"Bradd W. Szonye" <bradds@concentric.net> wrote:

>It's too late for additions to the standard, but it's not too late to
>suggest strictly-comforming extensions for use by vendors and
>experimenters, I guess.
>
>Ever since the "no binding temporaries to non-const references" rule, there
>have been complaints that some people want to use some objects that way
>intentionally. The canonical example is
>
>    ofstream("temp") << foo;
>
>where "foo" cannot be inserted by a member operator<<. Several suggestions
>have appeared for solving the problem, such as permitting ostream::operator
>ostream&() to override the default binding rules and making the above line
>legal.
>
[ "Mutable constructor" extension proposal deleted ]

The above problem can be solved in at least three ways without extending
the language and without using John Potter's lvalue() technique.

Solution 1:

template <class T> inline
ostream& operator<< (const ostream& os, const T& t)
{
  return const_cast<ostream&> (os) << t;
}

ostream("file") << foo;


Solution 2:

class tmp_ofstream
{
    ofstream imp;
  public:
    tmp_ofstream(string filename) : imp(filename) { }
    // Similarly for other constructors

    template <class T>
    ofstream& operator<< (const T& t) const
    {
      return const_cast<ofstream&>(imp) << t;
    }

    // Other forwarding functions here ...
};

tmp_ofstream("file") << foo;

The above techniques rely on the fact that a const ostream is a nearly
useless animal and that writing to an ostream should always be allowed,
even in the case of ostream temporaries. If you want to limit this
const-violation to temporary variables only, you will need a language
extention like the one suggested by Bradd W. Szonye.

Another solution is similar to John Potter's technique. However, since
the lvalue function is not templated, it cannot be used (and abused) for
other classes:

Solution 3:

inline ostream& lvalue(const ostream& os)
{
  return const_cast<ostream&>(os);
}

lvalue(ofstream("file)) << foo;


-------------------------------------------------------------
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
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1997/06/12
Raw View
James Kanze <james-albert.kanze@vx.cit.alcatel.fr> wrote in article
<rf53eqpk10g.fsf@vx.cit.alcatel.fr>...

In short...
My suggestion: declare constructors "mutable" to allow binding temporaries
to non-const references; John Potter's suggestion: write an "lvalue"
template to convert temporaries to usable lvalues.

>  |> Discussion of advantages and disadvantages of both?
>
> Well, in general: if a class has externally visible behavior (a la
> ofstream), then somewhere, sometime, it will be appropriate to bind a
> temporary of that type to a non-const reference.  If it doesn't have
> externally visible behavior (external to the class, that is), then
> calling a non-const function cannot be for side effects; it must be to
> change the value, which will then be discarded.
>
> In this sense, it is the class author that has the critical knowledge
> about whether it would make sense.  Which argues (IMHO, very strongly)
> against John Potter's suggestion.

Well, Mr. Potter's idea is still good for coping with libraries which
provide no other means for coping with the issue. It deserves mention (it's
a nice idiom), but it probably shouldn't be the primary means of dealing
with it.

> The problem with your suggestion is that the information is associated
> with a specific function, the constructor, and not with the class
> itself, where it belongs.  What happens, for example, if some
> constructors are mutable, and others not?

My intent was that the constructor used to create the temporary determines
whether it is "mutable" or not. This allows the class designer to say that,
for example, 'explicit foo::foo(string const & filename)' is mutable but
'foo::foo(int i)' is not. In other words, you can explicitly create a
mutable foo from a filename, but the compiler won't implicitly convert an
int to a foo and then discard the "changes." My question: is that actually
an advantage of this approach, or am I just over-designing the feature?

> Is a compiler generated copy constructor mutable or not?

I hadn't considered the compiler-generated constructors. I've slowly
returned to using them again, but a few months ago I took Lakos's advice to
heart and always wrote my constructors explicitly. Of course you CAN always
write the "automatic" constructors yourself, but I've found that doing so
is something of a typing and maintenance nightmare for classes with a lot
of state.

Note that having a mutable constructor means defining a constructor;
therefore, there will never be more than one compiler-generated
constructor, the copy-constructor. There are already several rules for
describing how the compiler writes an implicit copy-constructor, so it's
obvious at least where the rules would go in the standard.

Possibilities for the definition: (1) copy-constructors are not mutable
unless explicitly declared that way; (2) they are mutable if the default
constructor is defined and is mutable; (3) they are mutable if any (other)
constructor is mutable; (4) they are mutable unless any (other) constructor
is not mutable. Of the choices, #1 is my favorite: I prefer saying what you
mean explicitly whenever possible. #2 is a decent heuristic, but somehow it
strikes me as a bad rule; #3 is almost as good as #1, and #4 would probably
lead to trouble during code maintenance (when somebody accidentally or
intentionally adds a non-mutable constructor).

> A second, somewhat minor, problem with your suggestion is that at
> present, no classes (and certainly not ofstream) are currently declared
> mutable.  My experience would suggest, however, that there are a lot of
> classes which should be mutable; how do we retrofit them.

Well, this is one place where Mr. Potter's template comes in handy.

The biggest gap in the "mutable temporary constructor" idea comes from
third-party libraries. I intentionally defined it as a "pure extension"
and, presumably, a vendor who provided the syntax would also provide the
appropriate definitions in their own libraries (like <iostream>). [Of
course, such vendors would issue a non-fatal diagnostic upon seeing a
mutable constructor or on binding such a temporary, right?] You'd need the
lvalue template to deal with third-party libraries at first, but if a
(popular enough) vendor actually provided mutable constructors, the library
vendors would use it.

> Ideally, it
> should be possible to bind a temporary of class type with non-const
> functions or a non-trivial destructor to a non-const reference unless
> the class author has said otherwise.  (Just my opinion, of course.)

Unfortunately, every class has at least one non-const method: the
assignment operator. That makes it a poor heuristic unless you tighten the
rule somehow. Even value-like objects typically have a slew of non-const
operators and state-mutating methods.

Thanks for your comments! As usual, they are well-considered and lead to
further insights.
--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: jpotter@falcon.lhup.edu (John Potter)
Date: 1997/06/12
Raw View
On 11 Jun 97 08:32:23 GMT, James Kanze
<james-albert.kanze@vx.cit.alcatel.fr> wrote:

: "Bradd W. Szonye" <bradds@concentric.net> writes:

:  |> Bradd W. Szonye <bradds@concentric.net> wrote in article
:  |> <01bc727c$c8e9adc0$2d72adce@azguard>...
:  |>
:  |> Apologies for replying to my own post. I'm adding an alternative solution
:  |> suggested in another article.
:  |>
:  |> > Ever since the "no binding temporaries to non-const references" rule,
:  |> there
:  |> > have been complaints that some people want to use some objects that way
:  |> > intentionally. The canonical example is
:  |> >
:  |> >     ofstream("temp") << foo;
:  |> >
:  |> > My own suggestion: allow the use of the 'mutable' keyword on constructor
:  |> > declarations to indicate that temporaries generated by the constructor
:  |> can
:  |> > be bound to non-const (mutable) references.
:  |> >
:  |> >     class ofstream : public ostream {
:  |> >     public:
:  |> >         mutable ofstream(char const * name, open_mode m = out); // ...
:  |> >     };
:  |>
:  |> John Potter suggests instead (in another article):
:  |> > template <class T>
:  |> > T& lvalue (T const& t) {
:  |> >  return const_cast<T&>(t);
:  |> >  }
:  |> >Now your problem is solved explicitely
:  |> > lvalue(ofstream("a_file")) << t;
:  |> >works for either member or global.  And, YOU must say it.
:  |>
:  |> I like Mr. Potter's suggestion, because I agree that programmers should
:  |> explicitly state their intent whenever impossible. On the other hand, I
:  |> like my suggestion since it lets a library writer define which objects (and
:  |> which constructors) are appropriate for use as lvalues in the first place.
:  |> The lvalue template looks more promising for objects with "borderline"
:  |> reference semantics and the mutable constructor more appropriate for
:  |> objects (like proxies) which always have lvalue semantics.
:  |>
:  |> Discussion of advantages and disadvantages of both?

: Well, in general: if a class has externally visible behavior (a la
: ofstream), then somewhere, sometime, it will be appropriate to bind a
: temporary of that type to a non-const reference.  If it doesn't have
: externally visible behavior (external to the class, that is), then
: calling a non-const function cannot be for side effects; it must be to
: change the value, which will then be discarded.

: In this sense, it is the class author that has the critical knowledge
: about whether it would make sense.  Which argues (IMHO, very strongly)
: against John Potter's suggestion.

Lest "suggestion" be interpreted that I would like that template in
the standard library, let me clarify my opinion.

As I see it, the no binding of temporaries to non-const references is
there to prevent surprises.  I am quite satisfied with the rules the
way they are and am not suggesting any change.  Since "rules were made
to be broken", workarounds are needed.  The language provides the
features needed.  In this case:
 ofstream("temp") << foo;
can be coded as
 {
  ofstream temp("temp");
  temp << foo;
  }
or
 streamDump("temp", foo);
where streamDump is an obvious template function with the above block
as body.  Or
 lvalue(ofstream("temp")) << foo;
Given that the two templates are inlined, I doubt that the generated
code would be much different in the three cases.

I think this subject arises only to avoid using the first case.  I see
it as a matter of style and taste on the part of the user.  I don't
think that a library provider should dictate that taste, and it could
even produce surprises if it could happen implicitly to the user.  No
new capabilities are produced, just a shorthand for using the existing
ones.

I have never seen a problem which could not be solved by using a named
temporary.  Am I missing something?  I would be very interested in a
proxy example since unlike ofstream, the user should be ignorant of
the workings of proxies and even of their existence.

John
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: bparker@gil.com.au (Brian Parker)
Date: 1997/06/13
Raw View
On 11 Jun 97 08:32:23 GMT, James Kanze
<james-albert.kanze@vx.cit.alcatel.fr> wrote:
>...
>Well, in general: if a class has externally visible behavior (a la
>ofstream), then somewhere, sometime, it will be appropriate to bind a
>temporary of that type to a non-const reference.  If it doesn't have
>externally visible behavior (external to the class, that is), then
>calling a non-const function cannot be for side effects; it must be to
>change the value, which will then be discarded.
>
>In this sense, it is the class author that has the critical knowledge
>about whether it would make sense.  Which argues (IMHO, very strongly)
>against John Potter's suggestion.
>
>The problem with your suggestion is that the information is associated
>with a specific function, the constructor, and not with the class
>itself, where it belongs.  What happens, for example, if some
>constructors are mutable, and others not?  Is a compiler generated copy
>constructor mutable or not?
>
>A second, somewhat minor, problem with your suggestion is that at
>present, no classes (and certainly not ofstream) are currently declared
>mutable.  My experience would suggest, however, that there are a lot of
>classes which should be mutable; how do we retrofit them.  Ideally, it
>should be possible to bind a temporary of class type with non-const
>functions or a non-trivial destructor to a non-const reference unless
>the class author has said otherwise.  (Just my opinion, of course.)

Allowing operator T& ( ) {return *this;} to initialise non-const
references has basically the semantics you suggest except that it has
the opposite sense i.e. by default class temps cannot bind to nonconst
references and hence the class author must explicitly allow it. This
seems right to me based on the frequency of use of this feature,
though it does lead to the backward compatibility issue you point out-
but that issue already exists under the current draft rules.

The advantage of using operator T& for this purpose is that it is
already needed for other purposes (specifically lvalue-returning proxy
classes, and in another thread it was suggested that that usage will
probably be allowed for at the next standards meeting), and hence it
would only be a small extension to also allow it to return *this-
basically just modify the sentence in 12.3.2 that currently disallows
it.

Where this behaviour is not desired for particular objects, const
temporaries could be returned which would prevent the application of
the (non-const) operator T&(). (in fact, if converting constructors
could be declared to return const temporaries, that could be used to
achieve the effect of the suggested mutable constructors).

Ideally, IMHO, one would also be able to declare conversion operators
explicit to achieve the effect of John Potter's lvalue function, where
implicit conversions are not desired.

,Brian Parker (bparker@gil.com.au)
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1997/06/13
Raw View
Brian Parker <bparker@gil.com.au> wrote in article
<339fa5f8.31005447@news.ipswich.gil.com.au>...
>
> Where this behaviour is not desired for particular objects, const
> temporaries could be returned which would prevent the application of
> the (non-const) operator T&(). (in fact, if converting constructors
> could be declared to return const temporaries, that could be used to
> achieve the effect of the suggested mutable constructors).

(Hope I haven't trimmed too much context.) Sounds like a good idea--doing
it withing the existing language. However, unless I've misunderstood you,
doesn't that invoke the "two user-defined conversions" problem? Or do you
just write a function which looks like a constructor but actually isn't? Or
is the first one considered explicit? Details/examples, if you don't mind.
--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1997/06/06
Raw View
It's too late for additions to the standard, but it's not too late to
suggest strictly-comforming extensions for use by vendors and
experimenters, I guess.

Ever since the "no binding temporaries to non-const references" rule, there
have been complaints that some people want to use some objects that way
intentionally. The canonical example is

    ofstream("temp") << foo;

where "foo" cannot be inserted by a member operator<<. Several suggestions
have appeared for solving the problem, such as permitting ostream::operator
ostream&() to override the default binding rules and making the above line
legal.

My own suggestion: allow the use of the 'mutable' keyword on constructor
declarations to indicate that temporaries generated by the constructor can
be bound to non-const (mutable) references. It doesn't introduce a new
keyword, it's not currently legal syntax, it allows class designers to make
the decision at a per-constructor level. (The last is important because you
might want your explicit constructors mutable but not your conversion
constructors.)

For example:

    class ofstream : public ostream {
    public:
        mutable ofstream(char const * name, open_mode m = out); // ...
    };

With the 'mutable' constructor, the temp-file snippet above becomes legal
regardless of the type of foo, as well as

    int WriteLog(ostream&);
    WriteLog(ofstream("temp", ios::out | ios::app));

--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Bradd W. Szonye" <bradds@concentric.net>
Date: 1997/06/08
Raw View
Bradd W. Szonye <bradds@concentric.net> wrote in article
<01bc727c$c8e9adc0$2d72adce@azguard>...

Apologies for replying to my own post. I'm adding an alternative solution
suggested in another article.

> Ever since the "no binding temporaries to non-const references" rule,
there
> have been complaints that some people want to use some objects that way
> intentionally. The canonical example is
>
>     ofstream("temp") << foo;
>
> My own suggestion: allow the use of the 'mutable' keyword on constructor
> declarations to indicate that temporaries generated by the constructor
can
> be bound to non-const (mutable) references.
>
>     class ofstream : public ostream {
>     public:
>         mutable ofstream(char const * name, open_mode m = out); // ...
>     };

John Potter suggests instead (in another article):
> template <class T>
> T& lvalue (T const& t) {
>  return const_cast<T&>(t);
>  }
>Now your problem is solved explicitely
> lvalue(ofstream("a_file")) << t;
>works for either member or global.  And, YOU must say it.

I like Mr. Potter's suggestion, because I agree that programmers should
explicitly state their intent whenever impossible. On the other hand, I
like my suggestion since it lets a library writer define which objects (and
which constructors) are appropriate for use as lvalues in the first place.
The lvalue template looks more promising for objects with "borderline"
reference semantics and the mutable constructor more appropriate for
objects (like proxies) which always have lvalue semantics.

Discussion of advantages and disadvantages of both?

One obvious disadvantage of my idea: it requires a compiler extension. One
obvious disadvantage of the lvalue template: it "encourages" naive
developers to slap an lvalue() around casts they can't get to work and
don't really understand. (I'm not sure whether that's Machiavelli or Murphy
at work.)
--
Bradd W. Szonye
bradds@concentric.net
http://www.concentric.net/~Bradds
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]