 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Ian McCulloch Guest
|
Posted: Thu Nov 24, 2005 8:06 am Post subject: temporaries and copy ctor |
|
|
Hi,
In porting some code to g++-3.4, I realized I had, by accident, passed a
temporary of a type with no copy constructor, as a parameter to a function
requiring a const-reference. This isn't allowed: to bind a temporary to a
(const) reference, the copy constructor needs to be accessible.
In my particular case, I have a debugging macro that writes the value of an
expression to a stream, where the call to operator<<() is in the context of
the macro. It would be possible to use say boost::lexical_cast here, but
that requires that the insertion operator to be found by ADL. But it is
not always easy to achieve this (eg, for standard library classes that have
no predefined insertion operator). My rather kludgy solution was to make
instead a macro
#define CONVERT_TO_STRING(x)
(const_cast
so that it is possible to define the insertion operator in the namespace
where the macro expansion occurs, no ADL required.
But, g++-3.4 doesn't like this, because std::ostringstream has no copy
constructor. Argh! This seems to imply that it is not possible to create
a temporary of a type with no copy constructor! That sounds a bit crazy to
me, but nevermind.
Is there some workaround for this? The only thing I can think of is to
write a new class derived from ostream that reimplements std::ostringstream
but has a copy constructor (with something like a shared_ptr to the
buffer), but that seems like overkill. I thought about declaring the
ostringstream in the initializer of a 'for' loop, but that wont work
without seriously refactoring the debugging macro, as it currently requires
the string conversion to be some expression of type convertible to string
and I think it isn't possible to embed a loop in an expression.
Hmm, I just thought of a possible solution:
(untested)
std::string ReturnStringAndDelete(std::ostream& stream)
{
std::string Result(stream.str());
delete &dynamic_cast
return Result;
}
#define CONVERT_TO_STRING(x)
ReturnStringAndDelete((*(new std::ostringstream())) << (x))
That doesn't make me very proud though.
Cheers,
Ian McCulloch
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze Guest
|
Posted: Thu Nov 24, 2005 5:15 pm Post subject: Re: temporaries and copy ctor |
|
|
Ian McCulloch wrote:
[...]
| Quote: | #define CONVERT_TO_STRING(x)
(const_cast<std::ostringstream&>(std::ostringstream()) << (x)).str()
so that it is possible to define the insertion operator in the
namespace where the macro expansion occurs, no ADL required.
But, g++-3.4 doesn't like this, because std::ostringstream has
no copy constructor. Argh! This seems to imply that it is
not possible to create a temporary of a type with no copy
constructor! That sounds a bit crazy to me, but nevermind.
|
There's no problem creating the temporary. You can't bind a
temporary to a non const reference, copy constructor or not, and
you cannot bind it to a const reference unless you have a copy
constructor. (The first rule is at least 15 years old, and was
already implemented in CFront 2.1. But many compilers don't
enforce it yet.)
In this case, it is an entirely different rule which is
involved. According to §5.2.11/1: "[...] Conversions that can
be performed explicitly using const_cast are listed below. No
other conversions shall be performed explicitly using
const_cast." And all of the conversions listed require an
lvalue as parameter if the target type is a reference. Since
std::ostringstream() is not an lvalue, the const_cast is
illegal. Period. With or without a copy constructor.
| Quote: | Is there some workaround for this?
|
The usual solution for this sort of thing is something like:
static_cast< ostringstream& >( ostringstream().flush() << (x)
).str()
Note the parentheses -- it is the return value of the <<
operator (an ostream&) which is being converted to an
ostringstream&.
Note to that the problem of binding to a non-const reference is
solved by calling a member function which returns a non-const
reference. In the classical IO streams, the idiom was:
(ostringstream&)( ostringstream() << "" << (x) ).str()
The standardization committee changed the << operator for char
const* from a member to a free function, and broke this.
| Quote: | The only thing I can think of is to write a new class derived
from ostream that reimplements std::ostringstream but has a
copy constructor (with something like a shared_ptr to the
buffer), but that seems like overkill.
|
Well, I use a wrapper class in such cases, but generally because
I also have something significant to do in the destructor.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Michiel.Salters@tomtom.co Guest
|
Posted: Thu Nov 24, 2005 5:17 pm Post subject: Re: temporaries and copy ctor |
|
|
Ian McCulloch wrote:
| Quote: |
#define CONVERT_TO_STRING(x)
(const_cast<std::ostringstream&>(std::ostringstream()) << (x)).str()
so that it is possible to define the insertion operator in the namespace
where the macro expansion occurs, no ADL required.
But, g++-3.4 doesn't like this, because std::ostringstream has no copy
constructor. Argh! This seems to imply that it is not possible to create
a temporary of a type with no copy constructor! That sounds a bit crazy to
me, but nevermind.
|
It's wrong. You can create a temporary. However, to const_cast to a
non-const&,
it would first have to bind to a const&, and that part is impossible.
| Quote: | Is there some workaround for this?
|
#define CONVERT_TO_STRING(x)
(std::ostringstream().flush() << (x)).str()
HTH,
Michiel Salters
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Jörg Barfurth Guest
|
Posted: Thu Nov 24, 2005 5:18 pm Post subject: Re: temporaries and copy ctor |
|
|
Hi,
Ian McCulloch schrieb:
| Quote: | In porting some code to g++-3.4, I realized I had, by accident, passed a
temporary of a type with no copy constructor, as a parameter to a function
requiring a const-reference. This isn't allowed: to bind a temporary to a
(const) reference, the copy constructor needs to be accessible.
|
Yes.
| Quote: | #define CONVERT_TO_STRING(x)
(const_cast<std::ostringstream&>(std::ostringstream()) << (x)).str()
[...] g++-3.4 doesn't like this, because std::ostringstream has no copy
constructor. Argh! This seems to imply that it is not possible to create
a temporary of a type with no copy constructor! That sounds a bit crazy to
me, but nevermind.
|
No. You can create a temporary of such a type. But you can't bind it to
a reference. But you can call member functions on it. And inside such a
function *this is an lvalue (even a modifiable one).
| Quote: | Is there some workaround for this? The only thing I can think of is to
write a new class derived from ostream that reimplements std::ostringstream
but has a copy constructor (with something like a shared_ptr to the
buffer), but that seems like overkill.
|
You don't need to reimplement everything. It is enough to provide a way
to get a temporary of that type as lvalue:
template
struct Temporary
{
T value;
Temporary() : value() {}
// add (templated) c'tors with arguments as needed
T& lvalue() { return value; }
};
#define CONVERT_TO_STRING(x)
(Temporary<std::ostringstream>().lvalue() << (x)).str()
HTH, Jörg
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ian McCulloch Guest
|
Posted: Fri Nov 25, 2005 1:05 pm Post subject: Re: temporaries and copy ctor |
|
|
[email]Michiel.Salters (AT) tomtom (DOT) com[/email] wrote:
| Quote: |
Ian McCulloch wrote:
#define CONVERT_TO_STRING(x)
(const_cast<std::ostringstream&>(std::ostringstream()) << (x)).str()
so that it is possible to define the insertion operator in the namespace
where the macro expansion occurs, no ADL required.
But, g++-3.4 doesn't like this, because std::ostringstream has no copy
constructor. Argh! This seems to imply that it is not possible to
create
a temporary of a type with no copy constructor! That sounds a bit crazy
to me, but nevermind.
It's wrong. You can create a temporary. However, to const_cast to a
non-const&,
it would first have to bind to a const&, and that part is impossible.
|
No, I think that is not correct. It is not possible to do
std::ostringstream const& s = std::ostringstream();
either (g++ 3.4 reports that the copy constuctor is not accessible). The
const_cast is still illegal though, but that is a separate issue.
Cheers,
Ian
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Maxim Yegorushkin Guest
|
Posted: Fri Nov 25, 2005 6:20 pm Post subject: Re: temporaries and copy ctor |
|
|
Ian McCulloch wrote:
[]
| Quote: | #define CONVERT_TO_STRING(x)
(const_cast<std::ostringstream&>(std::ostringstream()) << (x)).str()
so that it is possible to define the insertion operator in the namespace
where the macro expansion occurs, no ADL required.
But, g++-3.4 doesn't like this, because std::ostringstream has no copy
constructor. Argh! This seems to imply that it is not possible to create
a temporary of a type with no copy constructor! That sounds a bit crazy to
me, but nevermind.
Is there some workaround for this?
|
In this particular case you can hack the c++ type system by exploiting
a stream's member function returning a reference. std::ostream::write()
in the scetch:
(static_cast
1)).str();
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze Guest
|
Posted: Sat Nov 26, 2005 2:39 pm Post subject: Re: temporaries and copy ctor |
|
|
[email]Michiel.Salters (AT) tomtom (DOT) com[/email] wrote:
| Quote: | Ian McCulloch wrote:
#define CONVERT_TO_STRING(x)
(const_cast<std::ostringstream&>(std::ostringstream()) << (x)).str()
so that it is possible to define the insertion operator in
the namespace where the macro expansion occurs, no ADL
required.
But, g++-3.4 doesn't like this, because std::ostringstream
has no copy constructor. Argh! This seems to imply that it
is not possible to create a temporary of a type with no copy
constructor! That sounds a bit crazy to me, but nevermind.
It's wrong. You can create a temporary. However, to const_cast
to a non-const&, it would first have to bind to a const&, and
that part is impossible.
|
That's not the problem here. He doesn't try to bind to a const
reference (or to any reference), and the compiler isn't allowed
to introduce references just because it feals like it. The
problem is simply that the standard lists things that a
const_cast can do, and the requirements for the parameters. And
all const_cast to reference type require an lvalue. Even if
ostringstream had a copy constructor, the const_cast would be
illegal.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ian McCulloch Guest
|
Posted: Sat Nov 26, 2005 4:38 pm Post subject: Re: temporaries and copy ctor |
|
|
kanze wrote:
| Quote: | Michiel.Salters (AT) tomtom (DOT) com wrote:
Ian McCulloch wrote:
#define CONVERT_TO_STRING(x)
(const_cast<std::ostringstream&>(std::ostringstream()) << (x)).str()
so that it is possible to define the insertion operator in
the namespace where the macro expansion occurs, no ADL
required.
But, g++-3.4 doesn't like this, because std::ostringstream
has no copy constructor. Argh! This seems to imply that it
is not possible to create a temporary of a type with no copy
constructor! That sounds a bit crazy to me, but nevermind.
It's wrong. You can create a temporary. However, to const_cast
to a non-const&, it would first have to bind to a const&, and
that part is impossible.
That's not the problem here. He doesn't try to bind to a const
reference (or to any reference), and the compiler isn't allowed
to introduce references just because it feals like it. The
problem is simply that the standard lists things that a
const_cast can do, and the requirements for the parameters. And
all const_cast to reference type require an lvalue. Even if
ostringstream had a copy constructor, the const_cast would be
illegal.
|
Thanks for the explanation. A followup question, to see if I understand:
Supposing ostringstream had a copy ctor. Or better still, lets do this with
std::string. Is this legal?
template
T const& const_ref(T const& x) { return x; }
const_cast<std::string&>(const_ref(std::string())) = "Hello, World!";
What about
const_cast<std::string&>(static_cast<std::string const&>(std::string())) =
"Hello, World!";
?
Cheers,
Ian
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Sun Nov 27, 2005 3:59 am Post subject: Re: temporaries and copy ctor |
|
|
Ian McCulloch wrote:
| Quote: | kanze wrote:
[email]Michiel.Salters (AT) tomtom (DOT) com[/email] wrote:
Ian McCulloch wrote:
#define CONVERT_TO_STRING(x)
(const_cast<std::ostringstream&>(std::ostringstream()) << (x)).str()
so that it is possible to define the insertion operator in
the namespace where the macro expansion occurs, no ADL
required.
But, g++-3.4 doesn't like this, because std::ostringstream
has no copy constructor. Argh! This seems to imply that it
is not possible to create a temporary of a type with no copy
constructor! That sounds a bit crazy to me, but nevermind.
It's wrong. You can create a temporary. However, to
const_cast to a non-const&, it would first have to bind to a
const&, and that part is impossible.
That's not the problem here. He doesn't try to bind to a
const reference (or to any reference), and the compiler isn't
allowed to introduce references just because it feals like it.
The problem is simply that the standard lists things that a
const_cast can do, and the requirements for the parameters.
And all const_cast to reference type require an lvalue. Even
if ostringstream had a copy constructor, the const_cast would
be illegal.
Thanks for the explanation. A followup question, to see if I understand:
Supposing ostringstream had a copy ctor.
|
I don't think it changes anything *in* this particular case.
There are a lot of cases where it does make a difference; as a
parameter to const_cast just doesn't happen to be one of them.
| Quote: | Or better still, lets do this with std::string. Is this
legal?
template
T const& const_ref(T const& x) { return x; }
const_cast
|
That's legal. The function const_ref returns a reference, and a
reference is an lvalue. In this case, I'd say you might as well
go all the way, and write:
template< typename T >
T& lvalue( T const& x ) { return const_cast< T& >( x ) ; }
Formally, this requires that the class in question have a copy
constructor (even though it isn't normally used); not all
compilers enforce this, however.
| Quote: | What about
const_cast<std::string&>(static_cast<std::string
const&>(std::string())) =
"Hello, World!";
?
|
I think that that is legal. There's a rule that says that if
you can write:
T1 x( someExpression ) ;
then static_cast< T1 >( someExpression ) is legal, and has the
same effect, except that the object created is a nameless
temporary. (I'm not sure allowing this particular case was
intentional, but it is a possibly inadvertant consequence of the
rule.)
--
James Kanze mailto: [email]james.kanze (AT) free (DOT) fr[/email]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ian McCulloch Guest
|
Posted: Sun Nov 27, 2005 5:51 pm Post subject: Re: temporaries and copy ctor |
|
|
James Kanze wrote:
| Quote: | Ian McCulloch wrote:
|
[snip]
| Quote: | Or better still, lets do this with std::string. Is this
legal?
template <typename T
T const& const_ref(T const& x) { return x; }
const_cast
That's legal. The function const_ref returns a reference, and a
reference is an lvalue. In this case, I'd say you might as well
go all the way, and write:
template< typename T
T& lvalue( T const& x ) { return const_cast< T& >( x ) ; }
Formally, this requires that the class in question have a copy
constructor (even though it isn't normally used); not all
compilers enforce this, however.
|
Right. This is in fact exactly the code I had originally, which broke with
g++ 3.4 and ostringstream because of the missing copy ctor. For my post to
c.l.c++.m I 'simplified' it and in doing so accidentally broke it a bit
more ;)
| Quote: |
What about
const_cast<std::string&>(static_cast<std::string
const&>(std::string())) =
"Hello, World!";
?
I think that that is legal. There's a rule that says that if
you can write:
T1 x( someExpression ) ;
then static_cast< T1 >( someExpression ) is legal, and has the
same effect, except that the object created is a nameless
temporary. (I'm not sure allowing this particular case was
intentional, but it is a possibly inadvertant consequence of the
rule.)
|
That is interesting. I suspected both would be legal, but I didn't realize
the static_cast would be legal for a different reason. Makes sense though,
I have become accustomed to thinking of xxx_cast<T> as a function, which is
wrong.
Back to the case where there is no copy constructor. And suppose also there
is no convenient member function we can use to get an lvalue from the
temporary, as in the stringstream().flush() trick. Does this work?
template <typename T>
struct base : T
{
T& get() { return *this; }
};
base<std::ostringstream>().get() << x
? g++ 3.4 likes it.
Cheers,
Ian
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Mon Nov 28, 2005 8:21 am Post subject: Re: temporaries and copy ctor |
|
|
Ian McCulloch wrote:
| Quote: | James Kanze wrote:
Back to the case where there is no copy constructor.
|
Just a nit, but technically speaking, there is always a copy
constructor. If you don't provide one, the compiler will.
I presume you mean "no accessible copy constructor."
| Quote: | And suppose also there is no convenient member function we can
use to get an lvalue from the temporary, as in the
stringstream().flush() trick. Does this work?
template <typename T
struct base : T
{
T& get() { return *this; }
};
base
? g++ 3.4 likes it.
|
As long as T has a default constructor, I see no problems.
--
James Kanze mailto: [email]james.kanze (AT) free (DOT) fr[/email]
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Martin Bonner Guest
|
Posted: Mon Nov 28, 2005 3:51 pm Post subject: Re: temporaries and copy ctor |
|
|
James Kanze wrote:
| Quote: | Ian McCulloch wrote:
James Kanze wrote:
Back to the case where there is no copy constructor.
Just a nit, but technically speaking, there is always a copy
constructor.
Not always |
| Quote: | If you don't provide one, the compiler will.
|
Not if it can't (because you have derived from boost::noncopyable for
example).
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
kanze Guest
|
Posted: Mon Nov 28, 2005 9:04 pm Post subject: Re: temporaries and copy ctor |
|
|
Martin Bonner wrote:
| Quote: | James Kanze wrote:
Ian McCulloch wrote:
James Kanze wrote:
Back to the case where there is no copy constructor.
Just a nit, but technically speaking, there is always a copy
constructor.
Not always
If you don't provide one, the compiler will.
Not if it can't (because you have derived from
boost::noncopyable for example).
|
There's still a declaration. If the compiler requires a
definition, however, it is an error.
Given the way things work, I'll admit that a reasonable program
can't tell the difference.
But the standard says it is there. And it does play a role in
overload resolution:
class Uncopiable
{
Uncopiable( Uncopiable const& ) ;
public:
Uncopiable() {}
} ;
struct Funny : private Uncopiable
{
Funny( int i = 0 ) ;
operator int() { return 0 ; }
} ;
void
f()
{
Funny one ;
Funny two( one ) ;
}
The compiler finds the copy constructor, which is an exact
match, and selects it in overload resolution. And then
generates an error that it cannot generate the definition.
Without a copy constructor, the compiler would choose the
implicit conversion to int, and use tue constructor with int.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Martin Bonner Guest
|
Posted: Tue Nov 29, 2005 3:51 pm Post subject: Re: temporaries and copy ctor |
|
|
kanze wrote:
| Quote: | Martin Bonner wrote:
James Kanze wrote:
Ian McCulloch wrote:
James Kanze wrote:
Back to the case where there is no copy constructor.
Just a nit, but technically speaking, there is always a copy
constructor.
Not always
If you don't provide one, the compiler will.
Not if it can't (because you have derived from
boost::noncopyable for example).
There's still a declaration. If the compiler requires a
definition, however, it is an error.
Given the way things work, I'll admit that a reasonable program
can't tell the difference.
But the standard says it is there. And it does play a role in
overload resolution:
|
[snip example proving the point]
Aaaargh!
No wonder committee members run away screaming if anyone suggests "just
change the name look-up rules to ...".
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|