const has file scope

P

pauldepstein

The C++ Primer (4th edition -- Lippman, Lajoie and Moo) explains that
(non-local) const variables have file scope rather than global scope
and can therefore be defined in header files.

In this context, what is meant by a "const variable"? The examples
with (for example)
const int x = 2; are clear enough. But how about references to
const or non-const pointers to const int? Do they have file scope
(enabling inclusion in header files) too? What exactly is this file
scope rule?

Thanks for your help.

Paul Epstein
 
P

Phlip

pauldepstein said:
The C++ Primer (4th edition -- Lippman, Lajoie and Moo) explains that
(non-local) const variables have file scope rather than global scope
and can therefore be defined in header files.

In this context, what is meant by a "const variable"? The examples
with (for example)
const int x = 2; are clear enough. But how about references to
const or non-const pointers to const int? Do they have file scope
(enabling inclusion in header files) too? What exactly is this file
scope rule?

The rule means: Given a header file "yo.h", you can write these inside it,
outside of any class:

const int x = 42;
static int y = 43;

You cannot write a simple 'int z' because the linker would see it twice,
once per translation unit (combination of a .cpp file and its included .h
files).

The 'y' does not link, and each translation unit gets its own private copy
to change.

Changing 'x' causes undefined behavior, so a non-static const at file scope
has no meaning. Hence the language simply adds the effects of a 'static' for
you.

And compilers did not formerly apply this rule, so you will find the 'static
const' idiom in lots of legacy code...

And the rule only applies to the "top level" const. A pointer to a const is
itself a changable pointer. A const pointer to const is not changable, but
it will be tricky to initialize correctly. (Different translation units
initialize their static and global variables in an undeterminable order.)

A reference is already const, so it does not apply. Add the static!
 
V

Victor Bazarov

The C++ Primer (4th edition -- Lippman, Lajoie and Moo) explains that
(non-local) const variables have file scope rather than global scope
and can therefore be defined in header files.

In this context, what is meant by a "const variable"? The examples
with (for example)
const int x = 2; are clear enough. But how about references to
const or non-const pointers to const int? Do they have file scope
(enabling inclusion in header files) too? What exactly is this file
scope rule?

Well, if you had to ask, don't you already know the answer? The object
that is declared "const" has internal linkage. A pointer is an object.
Is the pointer [itself] const? Yes? Then its linkage is internal.
No? Then the linkage probably isn't internal. A reference isn't an
object, and therefore cannot be cv-qualified.

There is no "file scope rule" in C++. There is linkage. It can be
external, it can be internal, or it can be absent. Namespace objects
declared 'const' have internal linkage (except for ones that are
explicitly declared 'extern' or have been already declared to have
external linkage). Please refer to Standard, [basic.link] (3.5 in the
current Standard).

V
 
A

Alf P. Steinbach

* (e-mail address removed):
The C++ Primer (4th edition -- Lippman, Lajoie and Moo) explains that
(non-local) const variables have file scope rather than global scope
and can therefore be defined in header files.

Oh, there's so much wrong with that statement!

Almost every word.

What I think was intended: that /namespace scope/ const variables have /internal
linkage/ (that is, linkage restricted to the compilation unit) /by default/, and
therefore can /easily/ and directly be defined in header files without running
afoul of the One Definition Rule.

In this context, what is meant by a "const variable"? The examples
with (for example)
const int x = 2; are clear enough.

Yep, that's it.

But how about references to
const or non-const pointers to const int?

If you write

int const& x = 2;

at namespace scope, then you have a reference with extern linkage.

You can refer to this reference from another compilation unit by declaring

extern const& x;

If you write

int const* p = 0;

at namespace scope, then you have a pointer with extern linkage.

If you write

int const* const p = 0;

at namespace scope, then you have a pointer with internal linkage.

What matters is the outermost level const'ness, the const'ness of the variable
itself.

***

A reference can't itself be const or non-const.

That means that to make a reference have internal linkage you need to apply the
'static' keyword, e.g.

static int const& x = 42;

In spite of this you can't write just

int const& x;

in order to declare but not define a reference with extern linkage. It's not a
declaration with internal linkage. It is simply invalid, by §8.5.3/3 which
states that the initialization of a reference can only be omitted in a parameter
declaration, for the result type of a routine, for a class member declaration,
or "where the extern specifier is explicitly used".

Do they have file scope
(enabling inclusion in header files) too?

That question is meaningless.

What exactly is this file scope rule?

That question is also meaningless :), but regarding what I think you mean, see
above.


Cheers & hth.,

- Alf
 
A

Alf P. Steinbach

* Phlip:
The rule means: Given a header file "yo.h", you can write these inside it,
outside of any class:

const int x = 42;
static int y = 43;

You cannot write a simple 'int z' because the linker would see it twice,
once per translation unit (combination of a .cpp file and its included .h
files).

Sort of OK. You can write

int z;

in a header file, but it would be impractical to write

int z = 44;

because that would restrict the header file to use in only one compilation unit.

The 'y' does not link, and each translation unit gets its own private copy
to change.

Changing 'x' causes undefined behavior, so a non-static const at file scope
has no meaning. Hence the language simply adds the effects of a 'static' for
you.

Sort of OK but the rationalization is wrong and misleading.

You can write

extern int const x;

and define x wherever (provided that there's only one definition).

In order to /define/ a constant with extern linkage in a header file you can
employ the templated constant idiom. So there's no technical reason why the
language doesn't support it directly. Rather, I suspect that the lack of
support, so that one has to employ a notational workaround, has to do with the
evolution of the language, the order in which features were introduced.


Cheers & hth.,

- Alf
 
V

Victor Bazarov

Alf said:
[..]
If you write

int const& x = 2;

at namespace scope, then you have a reference with extern linkage.

You can refer to this reference from another compilation unit by declaring

extern const& x;

Uh.. That probably was intended as

extern int const& x;

, I believe.

V
 
A

Alf P. Steinbach

* Victor Bazarov:
Alf said:
[..]
If you write

int const& x = 2;

at namespace scope, then you have a reference with extern linkage.

You can refer to this reference from another compilation unit by
declaring

extern const& x;

Uh.. That probably was intended as

extern int const& x;

, I believe.

Yep, thanks.

Cheers,

- Alf
 
J

James Kanze

The C++ Primer (4th edition -- Lippman, Lajoie and Moo)
explains that (non-local) const variables have file scope
rather than global scope and can therefore be defined in
header files.

Hmmm. That surprises me a bit. The book (at least in that
version) is recent enough that it shouldn't talk about file
scope (which doesn't exist in standard C++ -- it basically
disappeared with the introduction of namespaces).

It also surprises me, because const-ness has never affected
scope.
In this context, what is meant by a "const variable"?

A variable which is declared const? I can't think of any other
meaning to give it.
The examples with (for example)
const int x = 2; are clear enough. But how about references
to const or non-const pointers to const int?

Is the variable const? That's all that counts. Any other const
is irrelevant.
Do they have file scope (enabling inclusion in header files)
too?

I repeat, but there is no file scope in C++. It just doesn't
exist.
What exactly is this file scope rule?

Something that doesn't exist in C++.

Const-ness has no effect on scope. Neither on the scope of the
variable (i.e. what it's fully qualified name looks like) or the
scope it is visible in. (I can't think of a case where the two
are different for a variable, but friend declarations do
introduce functions into one scope, while only making them
visible in a different scope.) Const-ness does affect the
default linkage, but it is only one element which comes into
play. (A const variable declared at local scope has no linkage,
for example.)
 
J

James Kanze

The rule means: Given a header file "yo.h", you can write
these inside it, outside of any class:
const int x = 42;
static int y = 43;

Sure you can. If you do, you can't include the header in
multiple translation units, but that's a different issue.

For what it's worth, I have a couple of included files that do
contain such things. In this case, the use of an include is so
that I get different code, depending on the -I options passed to
the compiler (which in turn depends on the OS).
You cannot write a simple 'int z' because the linker would see
it twice, once per translation unit (combination of a .cpp
file and its included .h files).

I'm too lazy to check, but I don't think a diagnostic is
required in this case. (It wasn't in earliest C, and given the
way history lives on, it may not be today. All compilers I know
do give you one, however.)
The 'y' does not link, and each translation unit gets its own
private copy to change.
Changing 'x' causes undefined behavior, so a non-static const
at file scope has no meaning.

There's no such thing as file scope in C++. Perhaps you mean
namespace scope. In which case, I beg to differ:

#include <boost/array.hpp> // or simply <array> in C++0x

int const size = 43 ;
boost::array< int, size > array = { ... } ;

, for example. In fact, anything that uses the address of, or a
reference to the constant. (In many cases, it may just result
undefined behavior, rather than causing the code not to compile.
And in some of those cases, the resulting code will (seem to)
work.)

As a result, I've gradually been adopting the policy of
systematically using extern for all file scope variables.

(Note too:

int const i1 = 42 ;
enum { i2 = 42 } ;

inline void
f( std::vector< int >& dest )
{
dest.push_back( i1 ) ; // undefined behavior.
dest.push_back( i2 ) ; // no problem.
}

In practice, this is one case of undefined behavior I don't
worry too much about; I can't imagine an implementation where it
won't work in practice.)
Hence the language simply adds the effects of a 'static' for
you.

The rule was adopted a long, long time ago. It never really
made sense, but it didn't really cause any problems then, and
apparently, some programmers wouldn't use "int const" variables
in headers if they had to add the static. (My reaction would
have been to get rid of those programmers, but Bjarne is a more
tolerant than I am. A lot more tolerant.) Regretfully, it's an
idea that causes serious problems today, but changing the rule
would break far too much code to be feasible.
And compilers did not formerly apply this rule, so you will
find the 'static const' idiom in lots of legacy code...

Could you site one. It was present in the very first CFront
that made its way out of AT&T; it was present in the oldest g++
that I've used (1.49); and it was present in Zortech 1.0. (And
back then, those were the only C++ compilers in existance.) I'd
be very surprised if any compiler ever existed that didn't
implement it.

The presence of "static const" is probably due more to people
who favor legibility---say what you mean, and mean what you say.
And the rule only applies to the "top level" const. A pointer
to a const is itself a changable pointer. A const pointer to
const is not changable, but it will be tricky to initialize
correctly. (Different translation units initialize their
static and global variables in an undeterminable order.)

If the initialization is static, there should be no problem.
(On the other hand, I can't think of any use of a global const
pointer to const.)
A reference is already const, so it does not apply. Add the
static!

Curiously enough, that's not what the standard says. §3.5/3: "A
name having namespace scope has internal linkage if it is the
name of [...] --- an object or reference that is explicitly
declared const and neither explicitly declared extern nor
previously declared to have external linkage; or [...]". Of
course, I'm not sure what "explicitly declared const" means in
the case of a reference:
int &const ri = i ;
is clearly illegal, and
int const& ri = i ;
declares the int referred to as const, not the reference.

It looks like a defect in the standard to me. (It could apply
to something like:

typedef int& CIR ;
CIR const ir = i ;

.. But I find it difficult to believe that the standard really
meant to say that the above has internal linkage.)
 
J

James Kanze


[...]
Sort of OK. You can write
in a header file, but it would be impractical to write
int z = 44;
because that would restrict the header file to use in only one
compilation unit.

What's the difference? Including the header in more than one
translation unit results in undefined behavior in both cases.
About all that changes is the probability of your getting a link
time error, and even then, I think most compilers today will
give you one in both cases. G++ under Linux does, anyway. And
that's the only compiler I have access to at the moment. On the
other hand, in early (pre-standard) C, your statement would have
been true.
Sort of OK but the rationalization is wrong and misleading.
You can write
extern int const x;
and define x wherever (provided that there's only one
definition).
In order to /define/ a constant with extern linkage in a
header file you can employ the templated constant idiom. So
there's no technical reason why the language doesn't support
it directly. Rather, I suspect that the lack of support, so
that one has to employ a notational workaround, has to do with
the evolution of the language, the order in which features
were introduced.

History is very definitely involved. And the issue isn't
trivial. As you point out, the compiler must be able to handle
it today (in a template), so there's no real technical argument
against it; one could argue on more general grounds (risk of
conflicting initializations, etc.), but all of those arguments
also hold against the template idiom (unless the template has
been exported, of course).

Note that doing so would also eliminate the case of undefined
behavior I posted else thread (pushing the constant back into a
vector<int> in an inline function).
 
J

James Kanze

* (e-mail address removed):
Oh, there's so much wrong with that statement!
Almost every word.

Yes. Given the authors (all very highly respected experts), it
rather surprises me. I'd be interested in seeing what they
actually wrote; I suspect that the above is just the posters
misinterpretation of it. (If not, then I'd have to definitly
recommend against the book.)
What I think was intended: that /namespace scope/ const
variables have /internal linkage/ (that is, linkage restricted
to the compilation unit) /by default/, and therefore can
/easily/ and directly be defined in header files without
running afoul of the One Definition Rule.

Yes. It's using them (e.g. in a template or an inline function)
which will now run afoul of the One Definition Rule (which
requires not only that the tokens be identical, but also that
all names bind to the same entity, except in very limited
circumstances).

[...]
If you write
int const& x = 2;
at namespace scope, then you have a reference with extern linkage.

See else thread. The standard very clearly says that references
obey the same rules as variables in this case. (Of course, it's
far from clear what those rules are, when applied to
references.)
A reference can't itself be const or non-const.

typedef int& RI ;
RI const ri = i ;

The problem is that the standard says "A name having namespace
scope (3.3.5) has internal linkage if it is the name of [...]--
an object or reference that is explicitly declared const and
neither explicitly declared extern nor previously declared to
have external linkage; or[...]" If you can't explicitly declare
a reference const (and I'm of the opinion you can't---in my
example above, the standard says that the const is ignored),
then what does the use of "reference" in the above sentence
mean?

(One way or the other, I think it's a defect in the standard.
I'll raise the point in comp.std.c, if I don't forget.)
 
J

James Kanze

namespace scope, not file scope

Either way, it's wrong. Const-ness has absolutely no effect on
scope. It does affect default linkage, however.
A variable whose static type is const-qualified. For example:

Just curious, but why do you say "static type"? I wasn't aware
that variables could have "dynamic type". (Maybe there are some
template tricks I'm unaware of, but given that you don't have a
"variable" until the template is instantiated, and all types are
known, I can't think of one.)
std::string const application_name = "Widget Master 3000";
That's a tricky one. I think it would technically be OK.

I wish I knew. The standard has some very strange wording
concerning it, and I can't figure out the intent.
Not const.
In C, "file scope" basically means "not inside any function or
struct definition," although a C whiz would have to clarify
that.
[1] In C++, there is no formal concept of "file scope,"
but people use the phrase to mean "global scope." In the case
you've mentioned, the authors should have written "namespace
scope" instead. Whenever possible, just stay away from the
phrase "file scope," since it never really means what it
sounds like, even in C.
[1] In the following C snippet, does struct bar have file scope?

struct foo { struct bar { int i; } a_bar; };
struct bar unqualified_use_of_bar;

Not in C++. The above snippet isn't legal C++. (In C, it's
exactly equivalent to defining struct bar before defining foo,
and then using it in foo.)

The issue of scope is somewhat clouded by the fact that the word
can be used to refer to two different things. Consider:

class C { friend void f() ; } ;

void
g()
{
extern int i ;
} ;

In both cases, the "scope" of the entity being declared is ::,
i.e. ::f() and ::i. In both cases, the name is not visible at
this scope: in the first case, ::f is only visible in the class
C (unless ADN picks it up), and in the second, ::i is only
visible in f.

Of course, linkage is a separate issue, partially orthogonal.
 
A

Alf P. Steinbach

* James Kanze:
* Phlip:
pauldepstein wrote:
[...]
You cannot write a simple 'int z' because the linker would
see it twice, once per translation unit (combination of a
.cpp file and its included .h files).
Sort of OK. You can write
in a header file, but it would be impractical to write
int z = 44;
because that would restrict the header file to use in only one
compilation unit.

What's the difference?

You're right, thanks; as Bruce Eckel might have said, I was thinking in C. :)

For C89 the first is, AFAIK, a declaration and the second a definition.

For C++ the same applies when there is a storage or linkage specifier (i.e.
'extern' or 'static') but otherwise in C++ they're both definitions.

Including the header in more than one
translation unit results in undefined behavior in both cases.
About all that changes is the probability of your getting a link
time error, and even then, I think most compilers today will
give you one in both cases. G++ under Linux does, anyway. And
that's the only compiler I have access to at the moment. On the
other hand, in early (pre-standard) C, your statement would have
been true.

Isn't it true in C89? At least gcc and msvc both treat the first a pure
declaration and the second as a definition, when default-compiled as C. Current
MinGW gcc (which is version 3.4.5) does that whether -std=c89 or -std=c99.


[snip]
History is very definitely involved. And the issue isn't
trivial. As you point out, the compiler must be able to handle
it today (in a template), so there's no real technical argument
against it; one could argue on more general grounds (risk of
conflicting initializations, etc.), but all of those arguments
also hold against the template idiom (unless the template has
been exported, of course).

Note that doing so would also eliminate the case of undefined
behavior I posted else thread (pushing the constant back into a
vector<int> in an inline function).

Uhm, I didn't see that, please elaborate...


Cheers,

- Alf
 
A

Alf P. Steinbach

* James Kanze:
See else thread. The standard very clearly says that references
obey the same rules as variables in this case. (Of course, it's
far from clear what those rules are, when applied to
references.)

Well, as you point out below [snipped], it seems the standard is inconsistent.
And that actual compilers do what I say above. I'd be interested in whether
there is some compiler that gives that x internal linkage?


[snip]


Cheers,

- Alf
 
J

James Kanze

* James Kanze:
* Phlip:
pauldepstein wrote:
[...]
You cannot write a simple 'int z' because the linker would
see it twice, once per translation unit (combination of a
.cpp file and its included .h files).
Sort of OK. You can write
int z;
in a header file, but it would be impractical to write
int z = 44;
because that would restrict the header file to use in only
one compilation unit.
What's the difference?
You're right, thanks; as Bruce Eckel might have said, I was
thinking in C. :)
For C89 the first is, AFAIK, a declaration and the second a
definition.

For C89, I'm not really sure. In K&R C, at least in the
original implementation, "int z" resulted in something like a
Fortran named common in the object file. There wasn't really a
distinction between declarations and definitions; and global
data was handled more or less like template data is today. (The
more things change, the more they stay the same:).) Basically,
the linker just put everything with the same name at the same
address, reserved the largest requested size for it, and
initialized it with one of the initializations, chosen more or
less randomly.

I sort of remember C89 being formulated in a way that would
allow an implementation to either do this, or to behave like
C++, but I'm not really sure any more, and I'm too lazy to look
it up.
For C++ the same applies when there is a storage or linkage
specifier (i.e. 'extern' or 'static') but otherwise in C++
they're both definitions.
Isn't it true in C89? At least gcc and msvc both treat the
first a pure declaration and the second as a definition, when
default-compiled as C. Current MinGW gcc (which is version
3.4.5) does that whether -std=c89 or -std=c99.

As I said, I'm not sure about C89, but I think the standard was
designed to allow both implementations.

For that matter, multiple definitions (in different translation
units) in C++ are also undefined behavior; they don't require a
diagnostic. (I don't think that there's anything in the
standard which requires a diagnostic that couldn't be generated
by the compiler, before linking.) But we all know what to
expect from a quality implementation.
[snip]
History is very definitely involved. And the issue isn't
trivial. As you point out, the compiler must be able to handle
it today (in a template), so there's no real technical argument
against it; one could argue on more general grounds (risk of
conflicting initializations, etc.), but all of those arguments
also hold against the template idiom (unless the template has
been exported, of course).
Note that doing so would also eliminate the case of undefined
behavior I posted else thread (pushing the constant back into a
vector<int> in an inline function).
Uhm, I didn't see that, please elaborate...

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.
 
A

Alf P. Steinbach

* 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. 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...

For now a practical burden-on-programmer-for-each-individual-case solution is to
explicitly create a temporary,

dest.push_back( c+0 );

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.

But for that to work in a reasonable way one would need a distinction between
the current two rôles of 'inline'. For the rôle of optimization hint, let's call
that '[inline_expansion_hint]' (where I'm suggesting a C++0x attribute), one
wouldn't want any actual follow-up on the hint to produce an extra temporary,
that is, one wouldn't want a guaranteed added action that might be inefficient!
Whereas for the role of "folding" of definitions in separate translation units,
let's call that 'inline' (where I'm suggesting keeping that rôle of the existing
syntax), within that rôle one would want a binding to reference to const of
something that doesn't have external linkage, to act /as if/ a temporary was
produced, modulo the usual allowed elicision of copy construction.

The effect on existing code would be to remove one source of (I think they must
be relatively infrequent) bugs, and to possibly make some 'inline' routines
perform a nano-second slower or so. The effect on future code would be remove
one main source of confusion about the language, namely about the two rôles of
'inline', and to enable programming in confidence about correctness, plus to
enable more confident inline optimization hinting, and perhaps even forcing
inlining -- and existing practice, with numerous language extensions,
indicates a practical need for having that as part of the standard language.

Disclaimer: Since I'm not quite awake yet (need more coffee), please tell if I'm
being stupid here.


Cheers,

- Alf
 
J

James Kanze

* James Kanze:
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 ) ;
..)
 
A

Alf P. Steinbach

* James Kanze:
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.)


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.



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.




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 ) ;
.)

Well yeah I was stupid. As I wrote (but not quoted in above) I'd like to know if
I was, cause it was still early morning, and you answered that :). Hm.

rvalue reference as formal argument sounds OK if that can guarantee an rvalue
conversion.

Can it?

I seem to recall a recent rule change in that area.


Cheers,

- Alf (master of quoting)
 
S

SG

rvalue reference as formal argument sounds OK if that can guarantee an rvalue
conversion.

Can it?

No, it can't. It simply won't bind. If it did it wouldn't be any
different from pass-by-value.
I seem to recall a recent rule change in that area.

Yes. To summarize it:
* Rvalue references can't be initialized with lvalues anymore.
* But an lvalue reference can still be cast (static_cast)
to an rvalue reference (used in std::forward and std::move).

Cheers!
SG
 
J

James Kanze

[...]
Huh? The scope of f is C. There is no ::f in that code. The fully
qualified name is ::C::f, assuming C is ::C.

Did you not see the friend? §11.4: "The name of a friend is not
in the scope of the class." The function declared as friend is
::f(). The name itself is lexically scoped in C, however, and
if you try to use f() outside of C, without any other
declaration, the compiler won't find it. On the other hand, if
you use f(), then you'll have to define it somewhere. And if
you don't define it inline in the class, the definition will
have to be at namespace scope somewhere.
The scope of i is... Well, actually, I've never seen that
usage of extern before, so I'll take your word for it.

You've probably seen it, implicitly and unintentionally, for a
function:):

void
g()
{
std::vector< C > v( std::istream_iterator( file ),
std::istream_iterator() ) ;
} ;

(For functions, the extern is implicit if there is no
definition.) The above declares a function ::v (supposing g()
is at global scope), but the function itself is only visible in
g().

I'm not sure what vocabulary to use to make the distinction
clear, and I don't think that the standard is always as clear as
it should be in this regard. (I think it sometimes uses the
distinction "lexical scope" vs. "scope of the
entity/variable/function", when relevant. But most of the time,
it just says scope, and leaves it to you to figure out what is
meant.)
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,211
Latest member
NelleWilde

Latest Threads

Top