Re: const has file scope
On May 4, 5:34 am, "Alf P. Steinbach" <al...@start.no> wrote:
* James Kanze:
The one definition rule requires not only token identity, but
that all names bind to the same entity---the only exception is
for names that refer to something that is a constant integral
expression, in a context where there is an immediate lvalue to
rvalue conversion. Given:
int const c = 43 ;
inline void
f( std::vector< int >& dest )
{
dest.push_back( c ) ;
}
in a header, the c in "dest.push_back( c )" resolves to a
different entity in each translation unit (because c has
internal linkage), and because vector<>::push_back takes a
reference, there is no immediate lvalue to rvalue
conversion---just the opposite, you've effectively taken the
address. Which is a violation of the one definition rule.
Ouch! This is a hard one.
Yes, and I'm not sure what to do about it. In practice, it will
in fact always work as long as the function you call eventually
does make the lvalue to rvalue conversion, and use the value; it
will fail if the function takes the address of the reference,
and starts comparing addresses (since the address of the c
object will be different in different translation units). How
to formulate this distinction in standardese is not clear,
however, so the standard takes the easy way out: if there isn't
an immediate conversion, it's undefined behavior. (So:
int const c = 43 ;
inline void
f( std::vector< int >& dest )
{
int tmp = c ;
dest.push_back( tmp ) ;
}
is legal.)
In practice, I don't see how an implementation can actually make
this fail, and I tend to ignore the issue, at least in such
cases where it is clear that the called function will only use
the value (and the fact that the function takes a reference is
only because it is a template---had the function been written
exclusively for int, it would have used pass by value). But the
fact that I can't imagine an implementation in which it would
fail may just be due to a lack of imagination on my part. (If I
were writing a compiler, I'd try to arrange things so that as
many violations as possible of the one definition rule were
caught by the linker. But this one is difficult.)
Maybe a second posting to csc++ is
appropriate! For in C++0x the rule about creating temporaries
for binding-to-ref-to-const was changed: IIRC the compiler is
now required to not create a temporary when the actual arg is
an lvalue expression, a direct binding (which I knew impacted
use of std::auto_ptr, but I never thought of the above!),
which means that C++0x /enforces/ that the above will be a
problem...
Could you run that by me again? The compiler has never been
allowed to create a temporary when the initializer of a
reference (to const or not) is an lvalue. As far as I can see,
the wording for the case where the initializer of a reference is
an lvalue (and not a bit-field) is unchanged since C++98.
What may change things in some cases is the fact that there is
an overload of vector<>::push_back which takes an rvalue
reference. I don't think it's relevant here (but I haven't
really verified), because the int const should match the
push_back which takes the const lvalue reference, resulting in
exactly the same behavior as usual.
For now a practical
burden-on-programmer-for-each-individual-case solution is to
explicitly create a temporary,
dest.push_back( c+0 );
The old Fortran trick to "emulate" call be value. (In Fortran,
if you passed what would be an lvalue in C or C++, it could be
modified by the called function. If you passed what would be an
rvalue, no.) As I said, the more things change, the more they
remain the same.
I sort of prefer the named value, since it seems clearer. Sort
of---it's not immediately apparent in either case why you don't
use just c.
or perhaps even
template< typename T > T temporaryFrom( T x ) { return x; }
...
dest_push_back( temporaryFrom( c ) );
I think the *proper* solution, but perhaps it's much work, but
proper because it puts the burden on the compiler, would be to
change (again!) that rule about when the compiler is allowed
to create temporaries for binding to ref-to-const.
Again, I don't really see that point. A compiler is *not*
allowed to create a temporary when the initializer of a
reference is an lvalue. Ever. If I write something like:
int a ;
int const& b = a ;
I'm guaranteed that &b == &a, and that if I use b to initialize
other references, the referred to int has the lifetime of a,
even if b goes out of scope. (Not sure if the lifetime issue is
clear. Consider:
namespace { int i ; } ;
class C
{
public:
C( int const& init ) : myI( init ) {}
// ...
private:
int const& myI ;
} ;
C*
f()
{
int const& i1 = i ;
return new C( i1 ) ;
}
I'm guaranteed that I can use the returned C without problems;
that C::myI won't be a dangling reference. Which it would be if
I had written:
int const& i1 = i + 0 ;
return new C( i1 ) ;
..)
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34