Can static member variables be declared "inline"?

J

Juha Nieminen

As we know, the keyword "inline" is a bit misleading because its
meaning has changed in practice. In most modern compilers it has
completely lost its meaning of "a hint for the compiler to inline the
function if possible" (because if the compiler has the function
definition available at an instantiation point, it will estimate whether
to inline it or not, and do so if it estimates it would be beneficial,
completely regardless of whether the keyword 'inline' appears in the
function definition or not).

In fact, the keyword "inline" is in practice a linker command, not a
compiler command. It tells the linker that if it finds an instantiation
of that function in more than one object file, it should use only one of
them and discard the rest.

Also as we know, template functions and member functions of template
classes are implicitly "inline": If they are instantiated in more than
one compilation unit (and not inlined), the linker will use only one
instantiation when linking and discard the rest.

What many C++ programmers might not realize is that this implicit
inlining of template class members extends to static member variables as
well. In other words, if you do this:

// Template class header file
// --------------------------------------------
template<typename T>
class SomeClass
{
static int memberVariable;
...
};

template<typename T>
int SomeClass<T>::memberVariable = 1;
// --------------------------------------------

and this header is used in more than one compilation unit, the static
member variable will also be instantiated in all those compilation
units, but this will not cause a linker error. The linker will nicely
use only one of them and discard the rest. Effectively the static member
variable has been declared "inline".

This is not only a nice feature of template classes, but a must.
Without this property it would be impossible to have static member
variables in template classes because it would be impossible to
instantiate them in only one place (if the template class is used in
more than one compilation unit).

So my question is: Why can't static member variables of regular
classes be declared "inline", like the ones of template classes?
Compilers clearly have the support for this because they must do it for
template classes. However, seemingly the C++ standard does not support
this for static members of regular classes. Or is there a way that I
don't know of?
 
J

James Kanze

As we know, the keyword "inline" is a bit misleading because
its meaning has changed in practice. In most modern compilers
it has completely lost its meaning of "a hint for the compiler
to inline the function if possible" (because if the compiler
has the function definition available at an instantiation
point, it will estimate whether to inline it or not, and do so
if it estimates it would be beneficial, completely regardless
of whether the keyword 'inline' appears in the function
definition or not).

Unless the compiler can be reasonably sure of making a better
estimate than you, I'd call this a bug, or at least poor quality
of implementation. And only a very few compilers are capable of
this except in very limited cases.
In fact, the keyword "inline" is in practice a linker command,
not a compiler command.

It's not a command, it's a "function specifier", which is in
some ways a bit like a "storage class specifier". It's a part
of the language, and has a specific semantic associated with the
language (it makes multiple definitions legal, provided that
they are in different sources). It also has an "intent", and it
is a poor compiler which ignores this intent (unless, as I said,
it is certain that it can do better).
It tells the linker that if it finds an instantiation of that
function in more than one object file, it should use only one
of them and discard the rest.

No. That's one aspect of some implementations, but it's not
fundamental. (And a compiler could do this with non-inline
functions as well, if it wanted to.)
Also as we know, template functions and member functions of
template classes are implicitly "inline":

No they're not, although they can also be multiply defined (and
you're forgetting export, of course).
If they are instantiated in more than one compilation unit
(and not inlined), the linker will use only one instantiation
when linking and discard the rest.

Again, implementation defined. I've used compilers that didn't
instantiate any of the template functions until link time, and
then only once.
What many C++ programmers might not realize is that this
implicit inlining of template class members extends to static
member variables as well. In other words, if you do this:
// Template class header file
// --------------------------------------------
template<typename T>
class SomeClass
{
static int memberVariable;
...
};
template<typename T>
int SomeClass<T>::memberVariable = 1;
// --------------------------------------------
and this header is used in more than one compilation unit, the
static member variable will also be instantiated in all those
compilation units, but this will not cause a linker error.

Again, that's one way (not the best, but probably the most
widespread) of implementing this. It's certainly not required
by the standard.
The linker will nicely use only one of them and discard the
rest. Effectively the static member variable has been
declared "inline".
This is not only a nice feature of template classes, but a
must. Without this property it would be impossible to have
static member variables in template classes because it would
be impossible to instantiate them in only one place (if the
template class is used in more than one compilation unit).
So my question is: Why can't static member variables of
regular classes be declared "inline", like the ones of
template classes?

Because "the inline specifier indicates to the implementation
that inline substitution of the function body at the point of
call is to be preferred to the usual function call mechanism."
Which obviously doesn't make sense for variables. And "while an
implementation is not required to perform this inline
substitution", the intent is clearly expressed, and it would be
a very poor implementation which didn't perform it without a
very good reason for not doing so.
Compilers clearly have the support for this because they must
do it for template classes. However, seemingly the C++
standard does not support this for static members of regular
classes. Or is there a way that I don't know of?

Make the class a template:).

Seriously, why do you want to do it?
 
M

Marco Manfredini

James Kanze wrote:
[treat "inline" as "the definition is inlined into the interface"]
Seriously, why do you want to do it?

Header-only implementations of libraries (with globals) would be an
application.
 
C

cch@srdgame

于 Mon, 01 Sep 2008 22:26:08 +0000,Juha Nieminen写到:
As we know, the keyword "inline" is a bit misleading because its meaning
has changed in practice. In most modern compilers it has completely lost
its meaning of "a hint for the compiler to inline the function if
possible" (because if the compiler has the function definition available
at an instantiation point, it will estimate whether to inline it or not,
and do so if it estimates it would be beneficial, completely regardless
of whether the keyword 'inline' appears in the function definition or
not).

In fact, the keyword "inline" is in practice a linker command, not a
compiler command. It tells the linker that if it finds an instantiation
of that function in more than one object file, it should use only one of
them and discard the rest.

Also as we know, template functions and member functions of template
classes are implicitly "inline": If they are instantiated in more than
one compilation unit (and not inlined), the linker will use only one
instantiation when linking and discard the rest.

What many C++ programmers might not realize is that this implicit
inlining of template class members extends to static member variables as
well. In other words, if you do this:

// Template class header file
// -------------------------------------------- template<typename T>
class SomeClass
{
static int memberVariable;
...
};

template<typename T>
int SomeClass<T>::memberVariable = 1; //
--------------------------------------------

and this header is used in more than one compilation unit, the static
member variable will also be instantiated in all those compilation
units, but this will not cause a linker error. The linker will nicely
use only one of them and discard the rest. Effectively the static member
variable has been declared "inline".

This is not only a nice feature of template classes, but a must.
Without this property it would be impossible to have static member
variables in template classes because it would be impossible to
instantiate them in only one place (if the template class is used in
more than one compilation unit).

So my question is: Why can't static member variables of regular
classes be declared "inline", like the ones of template classes?
Compilers clearly have the support for this because they must do it for
template classes. However, seemingly the C++ standard does not support
this for static members of regular classes. Or is there a way that I
don't know of?

The one you decalared in template is not inline, even it is in hearder
file.

The template will be extracted to regular classes whenever you use
SomeClass<type>. Different "type" will be extracted to different class.

BTW: I only heard about inline function, but not inline variables.
 
J

Juha Nieminen

Alf said:
* James Kanze:

Practically, for the purpose of header-only code.

Right. If you make a very small utility class which, for example, you
want to distribute, it makes the usage of that utility much easier when
you can simply say "unpack this file into your source directory, and
then just #include it wherever you need it; nothing else is needed".

If the class happens to need a static member variable, then there's no
way around it: You must provide a source file (which might only contain
the definition of that static member variable and nothing else), and the
user will have to include this source file into his project or makefile
(or compile it separately). Sure, it's not that big of a deal, but adds
useless complication to such a small utility class, and can be a
nuisance for very small one-source-file programs.

But I suppose a trick around the problem is to indeed make the class a
template class and then create the actual type with a typedef. Perhaps
something like:

template<int i>
class TemplatedVersionOfTheClass
{
...
};

typedef TemplatedVersionOfTheClass<0> TheClass;

(In fact, this might even make it more efficient because only those
member functions which are really used will end up in the final
executable file. With regular classes all the member functions may well
end up there, even though some of them might never be called.)

Of course, depending on the compiler, error messages can get uglier.
 
J

Juha Nieminen

cch@srdgame said:
The one you decalared in template is not inline, even it is in hearder
file.

The template will be extracted to regular classes whenever you use
SomeClass<type>. Different "type" will be extracted to different class.

Wrong. Even if the same static member variable is instantiated in more
than one compilation unit (because both use the template class with the
same template parameters), the linker will use only one of those
instantiations and will not give an error because of multiple
definitions. This works in the same way as inline functions (which don't
actually get inlined).
 
J

James Kanze

* James Kanze:
In other words, the standard contains this wording because it
contains this wording?

The standard contains this wording because inline expresses an
intent which only makes sense for functions, not for variables.
If you really meant that then it would be meaningless.
But I think you misunderstood Juha's point.
The simple answer is, historic accident.
'inline' came first, templates came later, and the joining
wasn't perfect, just practical enough to be useful.

I'm pretty sure that that's not true. It's more or less an
(unfortunate) accident that templates work more or less like
inline does, and it was certainly never the intent to require
this, or ban more efficient (for the programmer)
implementations. And inline was always viewed more or less like
register, in the old days: an ugly hack, necessary because
compiler technology wasn't adequate to figure it out better than
the programmer. Probably, in ten years time, or more, inline
will be considered much like register is today---and be more or
less ignored. But we aren't there yet: inline is your way of
telling the compiler that the function will be called very, very
often, and that the execution time of the actual call will be
significant in the total runtime of the program. And that just
doesn't make sense for variables.
 
J

James Kanze


I don't think English is cch's native language, and his
expression isn't the clearest, but what he says is 100% correct
(although not necessarily very relevant to the issue at hand).
Even if the same static member variable is instantiated in more
than one compilation unit (because both use the template class with the
same template parameters),

Except that he explicitly said that the "type" were different.
the linker will use only one of those instantiations and will
not give an error because of multiple definitions. This works
in the same way as inline functions (which don't actually get
inlined).

And you seem to be confusing what the standard says about
"instantiation" and how some (not all) compilers happen to
implement it. As far as the standard is concerned, for any
given set of template arguments, instantiation takes place once,
and only once. Where exactly (in which of the various contexts
which triggered the instantiation) is not defined, and if the
results of the instantiation depend on where, the program has
undefined behavior, but there is one, and only one
instantiation. Some compilers do instantiate many times, and
count on the linker throwing out all but one, but the better
ones maintain a repository of some sort, and don't reinstantiate
if they don't have to. (Of course, getting this right is
non-trivial, and earlier versions often failed to reinstantiate
when they should have, or vice version, which has left a bad
taste in some people's mouth. But correctly implemented, it's
far superior than redoing the work n times, with n copies of the
same thing floating around.)
 
M

Marco Manfredini

James said:
James Kanze wrote:
[treat "inline" as "the definition is inlined into the interface"]
Seriously, why do you want to do it?
Header-only implementations of libraries (with globals) would
be an application.

But why would you want to do that? If your code is all
templates, you may have to, but it's really something which
you'd want to avoid, no?

Having the implementation and interface in one file is really handy,
because you don't have to maintain the build of the implementation file
(or even a separate library) anymore. Also it simplifies code sharing
between libraries or shared objects, because you are untroubled by
duplicate definition problems. Also, deployment is trivial and binary
compatibility a non-issue.
 
J

Juha Nieminen

James said:
Some compilers do instantiate many times, and
count on the linker throwing out all but one, but the better
ones maintain a repository of some sort, and don't reinstantiate
if they don't have to.

That would depend a lot on the compilation strategy used.

Classically each compilation unit (eg. .cc file) is compiled
completely separately and independently into an object file (eg. .o).
That's everything the compiler might be instructed to do: To compile one
..cc file into one .o file, period. The compiler does not know of
anything else. This is the most typical situation when compiling with
makefiles and the 'make' utility.

In this case the compiler has absolutely no way of knowing where else
some templated static variable might be used, so it simply has no other
option than to instantiate it in that object file. It knows of no
others. It can't even know what the object file will be used for. Maybe
it will not be used to link an executable directly, but rather create a
precompiled library (.a or .lib in many system) or a
dynamically-linkable one (.so, .dll). In theory the object file could
even be used to link it to a program written in a completely different
language (although only in theory, as in practice this happens quite
rarely). So in short, it has no other option than to make the
instantiation to the object file.

Is this an "inferior" compiler in your opinion? Because you talk about
other compilers which use a different compilation strategy as "better".
 
I

Ian Collins

James said:
Some compilers do instantiate many times, and
count on the linker throwing out all but one, but the better
ones maintain a repository of some sort, and don't reinstantiate
if they don't have to. (Of course, getting this right is
non-trivial, and earlier versions often failed to reinstantiate
when they should have, or vice version, which has left a bad
taste in some people's mouth.

"vice version" - I like it!
But correctly implemented, it's
far superior than redoing the work n times, with n copies of the
same thing floating around.)
That may or my not be true. In a distributed build environment,
obtaining and waiting for locks on a repository may impede progress more
than simply redoing the work n times. Some degree of duplication
removal will be required non-inlined copies of inline methods.
 
I

Ian Collins

Juha said:
That would depend a lot on the compilation strategy used.

Classically each compilation unit (eg. .cc file) is compiled
completely separately and independently into an object file (eg. .o).
That's everything the compiler might be instructed to do: To compile one
..cc file into one .o file, period. The compiler does not know of
anything else. This is the most typical situation when compiling with
makefiles and the 'make' utility.
Some compilers (Sun CC was one) used or still use a template repository
where each instantiation of a template has its own object file. These
are resolved just like "regular" object files at link time.
In this case the compiler has absolutely no way of knowing where else
some templated static variable might be used, so it simply has no other
option than to instantiate it in that object file.

Exactly - that might be a specific object for for that instantiation.
It knows of no
others. It can't even know what the object file will be used for. Maybe
it will not be used to link an executable directly, but rather create a
precompiled library (.a or .lib in many system) or a
dynamically-linkable one (.so, .dll). In theory the object file could
even be used to link it to a program written in a completely different
language (although only in theory, as in practice this happens quite
rarely). So in short, it has no other option than to make the
instantiation to the object file.
Object file or files. There is nothing in the standard that mandates a
one to one mapping between compilation units and object files. I don't
recall even seeing mention of object files.
Is this an "inferior" compiler in your opinion? Because you talk about
other compilers which use a different compilation strategy as "better".

A subjective opinion!
 
J

James Kanze

James said:
James Kanze wrote:
[treat "inline" as "the definition is inlined into the interface"]
Seriously, why do you want to do it?
Header-only implementations of libraries (with globals) would
be an application.
But why would you want to do that? If your code is all
templates, you may have to, but it's really something which
you'd want to avoid, no?
Having the implementation and interface in one file is really
handy, because you don't have to maintain the build of the
implementation file (or even a separate library) anymore. Also
it simplifies code sharing between libraries or shared
objects, because you are untroubled by duplicate definition
problems. Also, deployment is trivial and binary compatibility
a non-issue.

Which is all fine for toy projects, but anytime you modify a
header, you have to recompile all of the client code. So you
want as little as possible in the header---ideally, only the
API definitions. (Why do you think the compilation firewall
idiom is so popular?)
 
J

James Kanze

That would depend a lot on the compilation strategy used.
Exactly.

Classically each compilation unit (eg. .cc file) is compiled
completely separately and independently into an object file
(eg. .o).

And classically, the implementation code of the template was in
a separate source file, which didn't get read (and compiled)
until link time.
That's everything the compiler might be instructed to do: To
compile one .cc file into one .o file, period.

That's a fairly rare case today. I don't know of any compiler
which supports templates which uses it. Compilers output a
variety of files, for a variety of different reasons.
The compiler does not know of anything else. This is the most
typical situation when compiling with makefiles and the 'make'
utility.

It's not the situation with Sun CC, under Solaris. It wasn't
the situation with the old CC under HP/UX, either. (I've not
had the occasion to see what the newer aCC does). And if memory
serves me right (it's been a long time), it wasn't the case with
the old xlC compilers under AIX, and from what I've heard, it
isn't the case with IBM's Visual Age compilers either.

In fact, the only compiler I know which does it this way is g++,
and even then, there are options and variants which change it.
VC++ generates tons of additional files, but I don't think they
have anything to do with templates, so conceptually, VC++ is
probably similar to g++. Sun CC, HP's old CC and Visual Age CC
all use some form of repository, and cache instantiations.
In this case the compiler has absolutely no way of knowing
where else some templated static variable might be used, so it
simply has no other option than to instantiate it in that
object file.

It can look in the repository, and see if the template has been
instantiated with the given arguments or not. If it hasn't
been, the compiler instantiates it and adds the instantiation to
the repository (and NOT to the object file).

This is the way templates "classically" worked.
It knows of no others. It can't even know what the object file
will be used for. Maybe it will not be used to link an
executable directly, but rather create a precompiled library
(.a or .lib in many system) or a dynamically-linkable one
(.so, .dll). In theory the object file could even be used to
link it to a program written in a completely different
language (although only in theory, as in practice this happens
quite rarely). So in short, it has no other option than to
make the instantiation to the object file.

Which is why most compilers don't.
Is this an "inferior" compiler in your opinion?

In this regard, yes. (Obviously, other issues come into play,
and globally, g++'s support for templates is better than that in
Sun CC. But the compilation model used by Sun is far better.)
Because you talk about other compilers which use a different
compilation strategy as "better".

Well, using some sort of repository seems like a much better
solution, if done correctly. And most of the compilers I've
used have used a repository---g++ and VC++ are the notable
exceptions.
 
J

James Kanze

"vice version" - I like it!

A Freudian slip for vice versa, of course. But you've revealed
enough personal data in other postings for me to suspect that
you've some experience with early CFront based implementations
of templates (any of the Sun CC 3.x, for example), so you know
what I'm talking about: any unexplained error, and you zap the
repository and rebuild from scratch. (The files in the
repository weren't known to make, and the compiler didn't manage
its dependencies correctly---IIRC, it didn't look beyond the
first level of includes. Part of this was doubtlessly due to
the fact that all of the files in the repository had a
"standard" format: C++ source or object format, so the compiler
couldn't maintain all of the additional necessary information it
needed.)
That may or my not be true. In a distributed build
environment, obtaining and waiting for locks on a repository
may impede progress more than simply redoing the work n times.
Some degree of duplication removal will be required
non-inlined copies of inline methods.

That may be a point, and it's certainly relevant to the way Sun
implements its repository. But that sounds like a problem in
the implementation, and not something fundamental. If the
repository were in an Oracle database, for example, I doubt that
you'd find much slow down.
 
I

Ian Collins

James said:
But you've revealed
enough personal data in other postings for me to suspect that
you've some experience with early CFront based implementations
of templates (any of the Sun CC 3.x, for example), so you know
what I'm talking about: any unexplained error, and you zap the
repository and rebuild from scratch.

Indeed I do, I paid about 1500 pounds for a copy!
That may be a point, and it's certainly relevant to the way Sun
implements its repository. But that sounds like a problem in
the implementation, and not something fundamental. If the
repository were in an Oracle database, for example, I doubt that
you'd find much slow down.
As machines processing speed increases way faster than network speed,
anything that adds network writes is going to cause issues.
 
M

Marco Manfredini

James said:
Which is all fine for toy projects, but anytime you modify a
header, you have to recompile all of the client code. So you
want as little as possible in the header---ideally, only the
API definitions. (Why do you think the compilation firewall
idiom is so popular?)

The compilation firewall is popular, because there are Projects which
have a evolving, non-trivial implementation, but a fixed API. However,
there are also Projects with a trivial implementation and an evolving
interface - one example would be library/OS-syscall wrappers which
translate something from the "C" domain into a edible form (RAII,
typesafety, stl-adapter, exceptions...). There is no point in
maintaining a stable API, because the implementation is very unlikely
to change, which gives us the opportunity to evolve the interface as
needed, because the only one to upgrade to a new version is the one who
needs the new interface features anyway.

As a negative example, take the cairomm library, which is a wrapper
around the cairo-API, The encapsulation dogma is strong within their
maintainers and so they decided, that cairo_rotate must be wrapped as:

in the public header file:
---------------------------------------------
#include <cairo.h>
....
class Cairo {
....
void rotate(double angle_radians);
void rotate_degrees(double angle_radians);
...
}
---------------------------------------------

in the implementation file:
---------------------------------------------
void Context::rotate(double angle_radians)
{
cairo_rotate(m_cobject, angle_radians);
check_object_status_and_throw_exception(*this);
}
void Context::rotate_degrees(double angle_degrees)
{
cairo_rotate(m_cobject, angle_degrees * M_PI/180.0);
check_object_status_and_throw_exception(*this);
}
---------------------------------------------
Note the contempt for code reuse.

So basically, when I want to use cairomm, I have to dig into pkg-config
to find *the right shared library for my target platform* (multiarch!
cross-compile!) and when I run the program I need the runtime linker to
load it and relocate all calls into it. And I can't run my binary on a
system where cairomm is missing or, let's say, does not implement
rotate_degrees yet. I've got tangled up with a completely unnecessary
fill features library, which could have been implemented header only
instead.
 

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

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,154
Members
46,702
Latest member
LukasConde

Latest Threads

Top