 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
mlimber Guest
|
Posted: Thu Nov 10, 2005 12:28 pm Post subject: Suboptimal optimization |
|
|
In a recent thread on comp.lang.c++
([url]http://groups.google.com/group/comp.lang.c++/browse_frm/thread/033d97e6592e0562)[/url],
I posed a rather intricate problem, which boils down to the following:
A const static member of a template class is being optimized away by
the compiler. Unfortunately, the initialization of that member has a
global side effect that the compiler doesn't detect. Here's a
relatively simple example:
#include <iostream>
using namespace std;
int GetInvocationCount()
{
// Code with side-effect
static int i=0;
return ++i;
}
template<class T>
struct A
{
typedef T Type;
const static int i;
};
// Cause side effect
template<class T>
const int A<T>::i = GetInvocationCount();
// Instantiate the template
template A<int>;
// Instantiate the template again
struct B : A<float> {};
int main()
{
(void)B::i; // ***
cout << GetInvocationCount() << endl;
return 0;
};
If I run the program as is, I get 3 as expected. If, however, I run the
program after deleting the first line of main, I get 2 because the
instantiation of A
trivial, but in the other thread, it has serious consequences (viz., my
classes are not registered with my factory).
Obviously, I want to avoid this suboptimal optimization. Is this
standard-conformant behavior? Can anyone suggest a portable way to
avoid this problem?
Cheers! --M
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Ron Natalie Guest
|
Posted: Thu Nov 10, 2005 1:42 pm Post subject: Re: Suboptimal optimization |
|
|
mlimber wrote:
ot registered with my factory).
| Quote: |
Obviously, I want to avoid this suboptimal optimization. Is this
standard-conformant behavior? Can anyone suggest a portable way to
avoid this problem?
I believe the behavior you're seeing is legal. The implicit |
instantiation for the A<float> doesn't necessarily happen unless
you make an object of type B or somehow use the class in a way
that it would make a difference to the program. Your use of
it as a base class in an empty wrapper class is trivial enough
that one could argue that it doesn't make a difference to the
program.
You'll have to pull one of those kludges (either touch the
internals of the template or declare an object of the class).
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
mlimber Guest
|
Posted: Thu Nov 10, 2005 5:03 pm Post subject: Re: Suboptimal optimization |
|
|
Ron Natalie wrote:
| Quote: | mlimber wrote:
Obviously, I want to avoid this suboptimal optimization. Is this
standard-conformant behavior? Can anyone suggest a portable way to
avoid this problem?
I believe the behavior you're seeing is legal. The implicit
instantiation for the A<float> doesn't necessarily happen unless
you make an object of type B or somehow use the class in a way
that it would make a difference to the program. Your use of
it as a base class in an empty wrapper class is trivial enough
that one could argue that it doesn't make a difference to the
program.
|
The B class is trivial, but my real code, which demonstrates the same
problem, is not. See the other thread for a less trivial example. In
any case, my point is that the instantiation has a global side effect
that, ipso facto, makes it non-trivial and thus not suitable for
deletion by the pessimizer... er, optimizer. (Note that in these posts,
I am operating with compiler optimization entirely disabled via command
line switches.)
BTW, one of my compilers that this code must work on (VC6 with sp6)
warns if I add an explicit instantiation ("template A<float>;") in
addition to the inherited version ("warning C4660: template-class
specialization 'A<float>' is already instantiated"). If I ignore that
warning, the code generates the desired result (3), but if I "correct"
the code by removing the putatively redundant instantiation, I get the
undesired result (2). That's self-contradiction at its best.
| Quote: | You'll have to pull one of those kludges (either touch the
internals of the template or declare an object of the class).
|
I'm trying to avoid referencing each inherited instantiation (like
A<float>), except in the inheritance specification itself, because it's
a maintenance hassle especially when the number of classes grows large.
Cheers! --M
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Greg Herlihy Guest
|
Posted: Thu Nov 10, 2005 8:21 pm Post subject: Re: Suboptimal optimization |
|
|
mlimber wrote:
| Quote: | In a recent thread on comp.lang.c++
([url]http://groups.google.com/group/comp.lang.c++/browse_frm/thread/033d97e6592e0562)[/url],
I posed a rather intricate problem, which boils down to the following:
A const static member of a template class is being optimized away by
the compiler. Unfortunately, the initialization of that member has a
global side effect that the compiler doesn't detect. Here's a
relatively simple example:
#include <iostream
using namespace std;
int GetInvocationCount()
{
// Code with side-effect
static int i=0;
return ++i;
}
template
struct A
{
typedef T Type;
const static int i;
};
// Cause side effect
template
const int A
// Instantiate the template
template A<int>;
// Instantiate the template again
struct B : A<float> {};
|
No, this line is a declaration for the struct B. It is not an explicit
instantiation of A<float>. If it were, it would look like this:
template A<float>;
And in fact adding this explicit instantiation produces the desired
output of 3.
| Quote: | int main()
{
(void)B::i; // ***
cout << GetInvocationCount() << endl;
return 0;
};
If I run the program as is, I get 3 as expected. If, however, I run the
program after deleting the first line of main, I get 2 because the
instantiation of A
trivial, but in the other thread, it has serious consequences (viz., my
classes are not registered with my factory).
Obviously, I want to avoid this suboptimal optimization. Is this
standard-conformant behavior? Can anyone suggest a portable way to
avoid this problem?
|
There is no "optimization" being applied here. The problem is simply an
omission on the program's part. There are only two ways to instantiate
A<float>. The first is to allocate an A<float> or an object of a
derived class; the other way is to instruct the compiler to instantiate
A<float> explicitly. This program does neither.
Greg
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
mlimber Guest
|
Posted: Fri Nov 11, 2005 9:04 am Post subject: Re: Suboptimal optimization |
|
|
Greg Herlihy wrote:
| Quote: | mlimber wrote:
In a recent thread on comp.lang.c++
([url]http://groups.google.com/group/comp.lang.c++/browse_frm/thread/033d97e6592e0562)[/url],
I posed a rather intricate problem, which boils down to the following:
A const static member of a template class is being optimized away by
the compiler. Unfortunately, the initialization of that member has a
global side effect that the compiler doesn't detect. Here's a
relatively simple example:
#include <iostream
using namespace std;
int GetInvocationCount()
{
// Code with side-effect
static int i=0;
return ++i;
}
template
struct A
{
typedef T Type;
const static int i;
};
// Cause side effect
template
const int A
// Instantiate the template
template A<int>;
// Instantiate the template again
struct B : A<float> {};
No, this line is a declaration for the struct B. It is not an explicit
instantiation of A<float>. If it were, it would look like this:
template A<float>;
And in fact adding this explicit instantiation produces the desired
output of 3.
[snip] |
1. Why does my inheritance specification not qualify as an
instantiation of the template?
2. Can someone quote the relevant portion(s) of the Standard on this
point for me?
3. Is there a way to force the compiler to instantiate the base class
(A<float>) without explicitly referencing each specialization (e.g.,
"(void)A<float>::i;", "template A<float>;")?
Cheers! --M
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Alberto Ganesh Barbati Guest
|
Posted: Fri Nov 11, 2005 9:10 am Post subject: Re: Suboptimal optimization |
|
|
mlimber wrote:
| Quote: | In a recent thread on comp.lang.c++
([url]http://groups.google.com/group/comp.lang.c++/browse_frm/thread/033d97e6592e0562)[/url],
I posed a rather intricate problem, which boils down to the following:
A const static member of a template class is being optimized away by
the compiler. Unfortunately, the initialization of that member has a
global side effect that the compiler doesn't detect. Here's a
relatively simple example:
snip
If I run the program as is, I get 3 as expected. If, however, I run the
program after deleting the first line of main, I get 2 because the
instantiation of A<float> doesn't take place.
|
The compiler is correct. Line "struct B : A<float> {};" causes an
*implicit* instantiation of A<float>. According to 14.7.1/1:
"[...] The implicit instantiation of a class template specialization
causes the implicit instantiation of the declarations, but not of [...]
static data members [...]. [...] the specialization of the member is
implicitly instantiated when the specialization is referenced in a
context that requires the member definition to exist; in particular, the
initialization (and any associated side-effects) of a static data member
does not occur unless the static data member is itself used in a way
that requires the definition of the static data member to exist."
If you comment out line (***) which is the only reference to B::i, then
B::i need not be instantiated and therefore *must not* be instantiated.
It's not a matter of optimization, it's mandatory.
A more tricky question is: is the statement "(void)B::i;" a context that
"requires the definition of B::i to exists"? Compilers tends to
disagree, in fact, a lot of optimizers will detect that the statement
has no side-effects and will strip it entirely, thus also stripping the
initialization of B::i which *had* side-effects :-(
An even bigger problem is that the same optimizers also strip the
initialization of A::i which should not be stripped in any case,
according to 3.7.1/2. (For example VC7.1 falls in this category.)
(
HTH,
Ganesh
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Greg Herlihy Guest
|
Posted: Fri Nov 11, 2005 1:58 pm Post subject: Re: Suboptimal optimization |
|
|
mlimber wrote:
| Quote: | Greg Herlihy wrote:
mlimber wrote:
In a recent thread on comp.lang.c++
([url]http://groups.google.com/group/comp.lang.c++/browse_frm/thread/033d97e6592e0562)[/url],
I posed a rather intricate problem, which boils down to the following:
A const static member of a template class is being optimized away by
the compiler. Unfortunately, the initialization of that member has a
global side effect that the compiler doesn't detect. Here's a
relatively simple example:
#include <iostream
using namespace std;
int GetInvocationCount()
{
// Code with side-effect
static int i=0;
return ++i;
}
template
struct A
{
typedef T Type;
const static int i;
};
// Cause side effect
template
const int A
// Instantiate the template
template A<int>;
// Instantiate the template again
struct B : A<float> {};
No, this line is a declaration for the struct B. It is not an explicit
instantiation of A<float>. If it were, it would look like this:
template A<float>;
And in fact adding this explicit instantiation produces the desired
output of 3.
[snip]
1. Why does my inheritance specification not qualify as an
instantiation of the template?
|
Because this statement merely declares B as a struct type; it does not
declare any B type objects. The compiler will not decide to allocate B
objects on its own, it will only do so when it sees a B type object
declared somewhere in the program.
Instantiating templates works the same way. Unless the compiler
actually sees a B type variable in the flesh, it will not go ahead and
instantiate the A<float> template just because it's needed to create B
type objects.
| Quote: | 2. Can someone quote the relevant portion(s) of the Standard on this
point for me?
|
"Unless a class template specialization has been explicitly
instantiated (14.7.2) or explicitly specialized (14.7.3), the class
template specialization is implicitly instantiated when the
specialization is referenced in a context that requires a
completely-defined object type or when the completeness of the class
type affects the semantics of the program."
The declaration of B is not a context that requires the instantiation
of A<float>. Constructing a B struct, would be such a context however.
| Quote: | 3. Is there a way to force the compiler to instantiate the base class
(A<float>) without explicitly referencing each specialization (e.g.,
"(void)A<float>::i;", "template A<float>;")?
|
template A<float> will instantiate all methods and members of A<float>,
so, yes, it is not necessary to instantiate each member and method
explicitly individually.
Greg
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
mlimber Guest
|
Posted: Sat Nov 12, 2005 10:26 am Post subject: Re: Suboptimal optimization |
|
|
Alberto Ganesh Barbati wrote:
| Quote: | mlimber wrote:
In a recent thread on comp.lang.c++
([url]http://groups.google.com/group/comp.lang.c++/browse_frm/thread/033d97e6592e0562)[/url],
I posed a rather intricate problem, which boils down to the following:
A const static member of a template class is being optimized away by
the compiler. Unfortunately, the initialization of that member has a
global side effect that the compiler doesn't detect. Here's a
relatively simple example:
snip
If I run the program as is, I get 3 as expected. If, however, I run the
program after deleting the first line of main, I get 2 because the
instantiation of A<float> doesn't take place.
The compiler is correct. Line "struct B : A<float> {};" causes an
*implicit* instantiation of A<float>. According to 14.7.1/1:
"[...] The implicit instantiation of a class template specialization
causes the implicit instantiation of the declarations, but not of [...]
static data members [...]. [...] the specialization of the member is
implicitly instantiated when the specialization is referenced in a
context that requires the member definition to exist; in particular, the
initialization (and any associated side-effects) of a static data member
does not occur unless the static data member is itself used in a way
that requires the definition of the static data member to exist."
If you comment out line (***) which is the only reference to B::i, then
B::i need not be instantiated and therefore *must not* be instantiated.
It's not a matter of optimization, it's mandatory.
A more tricky question is: is the statement "(void)B::i;" a context that
"requires the definition of B::i to exists"? Compilers tends to
disagree, in fact, a lot of optimizers will detect that the statement
has no side-effects and will strip it entirely, thus also stripping the
initialization of B::i which *had* side-effects :-(
An even bigger problem is that the same optimizers also strip the
initialization of A::i which should not be stripped in any case,
according to 3.7.1/2. (For example VC7.1 falls in this category.)
(
|
Thanks, Alberto. That clarifies proper compiler behavior and proves
that my code shouldn't work on a compliant compiler.
So, my two remaining questions are:
1. Why? Can anyone explain the rationale for the behavior described in
14.7.1/1 with respect to side-effects? It's not clear to me why the
specified behavior is preferable to the one I expected.
2. Can anyone suggest a standard conformant way to force instantiation
of an inherited class like A<float> without listing each instantiation
explicitly? (Sneaky backdoors accepted.)
Cheers! --M
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Alberto Ganesh Barbati Guest
|
Posted: Sun Nov 13, 2005 3:39 pm Post subject: Re: Suboptimal optimization |
|
|
mlimber wrote:
| Quote: |
So, my two remaining questions are:
1. Why? Can anyone explain the rationale for the behavior described in
14.7.1/1 with respect to side-effects? It's not clear to me why the
specified behavior is preferable to the one I expected.
|
I'm not sure about it, but I guess it's the old C++ motto "you don't pay
for what you don't use".
| Quote: |
2. Can anyone suggest a standard conformant way to force instantiation
of an inherited class like A<float> without listing each instantiation
explicitly? (Sneaky backdoors accepted.)
|
The best trick I can think of is to refer to the member in the template
destructor. Unless you never destroy objects of the derived class (very
unlikely) the destructor of the base class will need to be instantiated,
so any reference in it to the static member will trigger the member
instantiation. For example:
template<class T>
struct A
{
typedef T Type;
const static int i;
~A() { (void)&i; } // force instantiation of i
};
I don't know if this is enough for those very aggressive optimizers I
described in my previous post, but it's definitely legal and should
accomplish the task on a conformant compiler.
HTH,
Ganesh
[ 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 14, 2005 2:36 pm Post subject: Re: Suboptimal optimization |
|
|
mlimber wrote:
| Quote: | Alberto Ganesh Barbati wrote:
mlimber wrote:
In a recent thread on comp.lang.c++
([url]http://groups.google.com/group/comp.lang.c++/browse_frm/thread/033d97e6592e0562)[/url],
I posed a rather intricate problem, which boils down to
the following:
A const static member of a template class is being
optimized away by the compiler. Unfortunately, the
initialization of that member has a global side effect
that the compiler doesn't detect. Here's a relatively
simple example:
snip
|
I think from a standards point of view, it counts as a use. The
compiler may (and probably will) optimize the actual statement
away, but the statement must trigger the instantiation of the
static variable.
| Quote: | An even bigger problem is that the same optimizers also
strip the initialization of A::i which should not be
stripped in any case, according to 3.7.1/2. (For example
VC7.1 falls in this category.)
(
Thanks, Alberto. That clarifies proper compiler behavior and
proves that my code shouldn't work on a compliant compiler.
So, my two remaining questions are:
1. Why? Can anyone explain the rationale for the behavior
described in 14.7.1/1 with respect to side-effects? It's not
clear to me why the specified behavior is preferable to the
one I expected.
|
I'm not sure of what the committee members had in mind, but I
can think of one possible reason: the desire to have the same
rules for data members and functions, and the fact that you
definitly don't want to instantiate a function unless it is
used. (Consider cases like std::list<>::sort.)
| Quote: | 2. Can anyone suggest a standard conformant way to force
instantiation of an inherited class like A<float> without
listing each instantiation explicitly? (Sneaky backdoors
accepted.)
|
Well, you're going to have to list the targetted types
somewhere; there are potentially an infinit number of types over
which the template can be instantiated, and the compiler cannot
instantiate them all. So some subset must be specified, and the
only way I know of for specifying such a subset is to list all
of its elements.
--
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 |
|
 |
mlimber Guest
|
Posted: Tue Nov 15, 2005 3:51 pm Post subject: Re: Suboptimal optimization |
|
|
kanze wrote:
| Quote: | mlimber wrote:
Alberto Ganesh Barbati wrote:
mlimber wrote:
A const static member of a template class is being
optimized away by the compiler. Unfortunately, the
initialization of that member has a global side effect
that the compiler doesn't detect. Here's a relatively
simple example:
I think from a standards point of view, it counts as a use. The
compiler may (and probably will) optimize the actual statement
away, but the statement must trigger the instantiation of the
static variable.
|
According to 14.7.1/1, as quoted by Ganesh above in this thread:
"[...] The implicit instantiation of a class template specialization
causes the implicit instantiation of the declarations, but not of [...]
static data members [...]. [...] the specialization of the member is
implicitly instantiated when the specialization is referenced in a
context that requires the member definition to exist; in particular,
the initialization (and any associated side-effects) of a static data
member does not occur unless the static data member is itself used in a
way that requires the definition of the static data member to exist."
In short, side-effects of implicit instantiations (like my
instantiation in the inheritance specification) may be ignored.
[snip]
| Quote: | 1. Why? Can anyone explain the rationale for the behavior
described in 14.7.1/1 with respect to side-effects? It's not
clear to me why the specified behavior is preferable to the
one I expected.
I'm not sure of what the committee members had in mind, but I
can think of one possible reason: the desire to have the same
rules for data members and functions, and the fact that you
definitly don't want to instantiate a function unless it is
used. (Consider cases like std::list<>::sort.)
|
I understand that the compiler should not instantiate functions or
static variables that are not used, but you seem to agree above with my
expectation that the side-effect should still occur. Functions, of
course, cannot have side-effects if they are not used, so it seems to
me that the rules *ought* to be different (if indeed having the same
rules was the committee's motivation for the behavior in question)
because of the different nature of variables and functions.
| Quote: | 2. Can anyone suggest a standard conformant way to force
instantiation of an inherited class like A<float> without
listing each instantiation explicitly? (Sneaky backdoors
accepted.)
Well, you're going to have to list the targetted types
somewhere; there are potentially an infinit number of types over
which the template can be instantiated, and the compiler cannot
instantiate them all. So some subset must be specified, and the
only way I know of for specifying such a subset is to list all
of its elements.
|
Right, but I would like to list the targeted types only in my
inheritance specifications, as in the OP.
Cheers! --M
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
mlimber Guest
|
Posted: Wed Nov 16, 2005 12:41 am Post subject: Re: Suboptimal optimization |
|
|
Alberto Ganesh Barbati wrote:
| Quote: | mlimber wrote:
[snip]
2. Can anyone suggest a standard conformant way to force instantiation
of an inherited class like A<float> without listing each instantiation
explicitly? (Sneaky backdoors accepted.)
The best trick I can think of is to refer to the member in the template
destructor. Unless you never destroy objects of the derived class (very
unlikely) the destructor of the base class will need to be instantiated,
so any reference in it to the static member will trigger the member
instantiation. For example:
template<class T
struct A
{
typedef T Type;
const static int i;
~A() { (void)&i; } // force instantiation of i
};
I don't know if this is enough for those very aggressive optimizers I
described in my previous post, but it's definitely legal and should
accomplish the task on a conformant compiler.
[snip] |
Thanks for the reply. Using your code, wouldn't A<>::~A() only be
instantiated under the same circumstances as A<>::i? If so, that
appears to leave me in the same position as before since listing A<> in
an inheritance specification for another class doesn't count as an
explicit instantiation. (Practically speaking, this doesn't appear to
work on my two test-beds -- VC6 with sp6 and g++ 3.4.1, though
admittedly neither is highly conformant.)
Cheers! --M
[ 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: Wed Nov 16, 2005 12:48 pm Post subject: Re: Suboptimal optimization |
|
|
mlimber wrote:
| Quote: | kanze wrote:
mlimber wrote:
Alberto Ganesh Barbati wrote:
mlimber wrote:
A const static member of a template class is being
optimized away by the compiler. Unfortunately, the
initialization of that member has a global side effect
that the compiler doesn't detect. Here's a relatively
simple example:
|
Did something get lost here? Or did I just get confused
concerning the context.
| Quote: | I think from a standards point of view, it counts as a use.
The compiler may (and probably will) optimize the actual
statement away, but the statement must trigger the
instantiation of the static variable.
According to 14.7.1/1, as quoted by Ganesh above in this thread:
"[...] The implicit instantiation of a class template specialization
causes the implicit instantiation of the declarations, but not of [...]
static data members [...]. [...] the specialization of the member is
implicitly instantiated when the specialization is referenced in a
context that requires the member definition to exist; in particular,
the initialization (and any associated side-effects) of a static data
member does not occur unless the static data member is itself used in a
way that requires the definition of the static data member to exist."
|
Attention: this has nothing to do with "optimization". Unless
the static member is used, the compiler is not allowed to
instantiate it. It's not "optimizing the actual statement
away", but compiling what the standard requires for that
statement.
In fact, that's probably what confused me with regards to the
context. The definition of a static variable of a class
template does not, in itself, generate any code that the
compiler can optimize away -- the only code is in the
instantiations. Now, suppose that somewhere you have a
statement which uses the static variable, something like
&A<int>::i. The compiler can optimize that statement away, if
it can determine that doing so doesn't have any effect on the
observable behavior of the program (and if that is the entire
statement, determining that is trivial). Even if it optimizes
the statement away, however, it must still instantiate the
template variable.
| Quote: | In short, side-effects of implicit instantiations (like my
instantiation in the inheritance specification) may be
ignored.
|
No. All "side-effects" of an instantiation, implicit or
explicit, must occur. The problem here is that there is no
instantiation, so no side-effects.
| Quote: | [snip]
1. Why? Can anyone explain the rationale for the behavior
described in 14.7.1/1 with respect to side-effects? It's
not clear to me why the specified behavior is preferable
to the one I expected.
I'm not sure of what the committee members had in mind, but
I can think of one possible reason: the desire to have the
same rules for data members and functions, and the fact that
you definitly don't want to instantiate a function unless it
is used. (Consider cases like std::list<>::sort.)
I understand that the compiler should not instantiate
functions or static variables that are not used, but you seem
to agree above with my expectation that the side-effect should
still occur.
|
If the code, as written, requires the instantiation of the
function of variable, then any "side-effects" of that
instantiation must occur.
But I'm wondering what you really mean. This is the first time
I've heard the expression "side-effects" applied to an
instantiation. Side-effects are normally what happens at
run-time; instantiation is a compile time phenomena.
| Quote: | Functions, of course, cannot have side-effects if they are not
used, so it seems to me that the rules *ought* to be different
(if indeed having the same rules was the committee's
motivation for the behavior in question) because of the
different nature of variables and functions.
|
I like orthogonality. It's hard enough explaining when
something is intantiated, and when it isn't, without having two
distinct sets of rules.
It's very important to realize that this is not an optimization
issue. A function is either instantiated, or it isn't. If it
is instantiated, all template functions and/or classes it uses
also get instantiated, and all functions, types and objects that
it uses must exist, or it is an error, and the code won't
compile. For this reason, it is very important that functions
like std::list<>::sort not be instantiated unless they are used;
instantiating std::list<>::sort will result in a compiler error
if the type doesn't support '<', and many types that you put in
lists won't support '<'.
It's less obvious, but the same logic applies to static
variables. Suppose the initialization expression of the static
variable uses '<' on the instantiation type. Instantiating a
template may trigger other instantiations and introduce
constraints on the instantiation that are not wanted in every
case.
| Quote: | 2. Can anyone suggest a standard conformant way to force
instantiation of an inherited class like A
listing each instantiation explicitly? (Sneaky backdoors
accepted.)
Well, you're going to have to list the targetted types
somewhere; there are potentially an infinit number of types
over which the template can be instantiated, and the
compiler cannot instantiate them all. So some subset must
be specified, and the only way I know of for specifying such
a subset is to list all of its elements.
Right, but I would like to list the targeted types only in my
inheritance specifications, as in the OP.
|
So create a "side effect" in your inheritance specification
which triggers the instantiation of the static variable.
Alberto's suggestion of refering to the static variable in the
destructor, for example, will ensure instantiation of the static
variable any time the destructor is instantiated. And if the
class is used as a base class, the destructor *will* be
instantiated (since it is implicitly called by the destructor of
the derived class).
--
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 |
|
 |
mlimber Guest
|
Posted: Tue Nov 22, 2005 1:21 am Post subject: Re: Suboptimal optimization |
|
|
kanze wrote:
| Quote: | mlimber wrote:
kanze wrote:
mlimber wrote:
Alberto Ganesh Barbati wrote:
mlimber wrote:
A const static member of a template class is being
optimized away by the compiler. Unfortunately, the
initialization of that member has a global side effect
that the compiler doesn't detect. Here's a relatively
simple example:
Did something get lost here? Or did I just get confused
concerning the context.
I think from a standards point of view, it counts as a use.
The compiler may (and probably will) optimize the actual
statement away, but the statement must trigger the
instantiation of the static variable.
According to 14.7.1/1, as quoted by Ganesh above in this thread:
"[...] The implicit instantiation of a class template specialization
causes the implicit instantiation of the declarations, but not of [...]
static data members [...]. [...] the specialization of the member is
implicitly instantiated when the specialization is referenced in a
context that requires the member definition to exist; in particular,
the initialization (and any associated side-effects) of a static data
member does not occur unless the static data member is itself used in a
way that requires the definition of the static data member to exist."
Attention: this has nothing to do with "optimization". Unless
the static member is used, the compiler is not allowed to
instantiate it. It's not "optimizing the actual statement
away", but compiling what the standard requires for that
statement.
In fact, that's probably what confused me with regards to the
context. The definition of a static variable of a class
template does not, in itself, generate any code that the
compiler can optimize away -- the only code is in the
instantiations. Now, suppose that somewhere you have a
statement which uses the static variable, something like
&A<int>::i. The compiler can optimize that statement away, if
it can determine that doing so doesn't have any effect on the
observable behavior of the program (and if that is the entire
statement, determining that is trivial). Even if it optimizes
the statement away, however, it must still instantiate the
template variable.
|
Clarified. Thanks. The revised title for this post should be
"Suboptimal non-instantiation", but it just doesn't have the same ring.
:-)
| Quote: | In short, side-effects of implicit instantiations (like my
instantiation in the inheritance specification) may be
ignored.
No. All "side-effects" of an instantiation, implicit or
explicit, must occur. The problem here is that there is no
instantiation, so no side-effects.
|
I guess the knot in my thinking has been that I attempt to instantiate
the class A<> (cf. the OP), but since A<>::i is not instantiated as a
result unless it is also explicitly (if trivially) used somewhere, the
side-effect associated with the initialization of A<>::i does not take
place. I had assumed it would.
| Quote: | [snip]
1. Why? Can anyone explain the rationale for the behavior
described in 14.7.1/1 with respect to side-effects? It's
not clear to me why the specified behavior is preferable
to the one I expected.
I'm not sure of what the committee members had in mind, but
I can think of one possible reason: the desire to have the
same rules for data members and functions, and the fact that
you definitly don't want to instantiate a function unless it
is used. (Consider cases like std::list<>::sort.)
I understand that the compiler should not instantiate
functions or static variables that are not used, but you seem
to agree above with my expectation that the side-effect should
still occur.
If the code, as written, requires the instantiation of the
function of variable, then any "side-effects" of that
instantiation must occur.
But I'm wondering what you really mean. This is the first time
I've heard the expression "side-effects" applied to an
instantiation. Side-effects are normally what happens at
run-time; instantiation is a compile time phenomena.
|
I mean that each compile-time instantiation will have a run-time
side-effect. In fact, that is precisely what my real code tried to
achieve: registering some class with an object factory (a la _Modern
C++ Design_) at run-time as a side-effect of instantiating a static
data member. (See the new code at the bottom of this post.)
| Quote: | Functions, of course, cannot have side-effects if they are not
used, so it seems to me that the rules *ought* to be different
(if indeed having the same rules was the committee's
motivation for the behavior in question) because of the
different nature of variables and functions.
I like orthogonality. It's hard enough explaining when
something is intantiated, and when it isn't, without having two
distinct sets of rules.
|
Agreed, but still, the rules should be as simple as possible but no
simpler.
| Quote: | It's very important to realize that this is not an optimization
issue. A function is either instantiated, or it isn't. If it
is instantiated, all template functions and/or classes it uses
also get instantiated, and all functions, types and objects that
it uses must exist, or it is an error, and the code won't
compile. For this reason, it is very important that functions
like std::list<>::sort not be instantiated unless they are used;
instantiating std::list<>::sort will result in a compiler error
if the type doesn't support '<', and many types that you put in
lists won't support '<'.
It's less obvious, but the same logic applies to static
variables. Suppose the initialization expression of the static
variable uses '<' on the instantiation type. Instantiating a
template may trigger other instantiations and introduce
constraints on the instantiation that are not wanted in every
case.
|
Ah ha. This seems to be what I was looking for as far as a rationale
for the rule, and now it makes sense. The only obstacle left is to get
around it as portably as possible.. ;-)
| Quote: | 2. Can anyone suggest a standard conformant way to force
instantiation of an inherited class like A
listing each instantiation explicitly? (Sneaky backdoors
accepted.)
Well, you're going to have to list the targetted types
somewhere; there are potentially an infinit number of types
over which the template can be instantiated, and the
compiler cannot instantiate them all. So some subset must
be specified, and the only way I know of for specifying such
a subset is to list all of its elements.
Right, but I would like to list the targeted types only in my
inheritance specifications, as in the OP.
So create a "side effect" in your inheritance specification
which triggers the instantiation of the static variable.
Alberto's suggestion of refering to the static variable in the
destructor, for example, will ensure instantiation of the static
variable any time the destructor is instantiated. And if the
class is used as a base class, the destructor *will* be
instantiated (since it is implicitly called by the destructor of
the derived class).
|
I have tried this and it works on VC++ 6 with sp6 and g++ 3.4.4, but
not with one essential element in my chain, viz. Texas Instruments C++
v.5.1.0, which I believe uses the EDG front-end. Is this a
bug/non-conformancy in their compiler? Here's the code that I used to
test it:
#include <cassert>
#include <memory> // For auto_ptr
/////////////////////////////////////////////////////////////
// Factory class simplified from Loki::Factory
template<class Base, class Derived>
class Factory
{
typedef Base* (*CreationFn)();
CreationFn creationFn_;
Factory() : creationFn_(0) {}
public:
static Factory& Instance() { static Factory f; return f; }
bool Register( const CreationFn creationFn )
{
creationFn_ = creationFn;
return true;
}
Base* Create() const
{
assert( creationFn_ != 0 );
return creationFn_();
}
};
/////////////////////////////////////////////////////////////
// This policy class automatically registers a Base/Derived
// pair with the appropriate factory.
template<class Base, class Derived>
class FactoryRegistrar
{
typedef Factory<Base,Derived> MyFactory;
static const bool isRegistered_;
protected:
virtual ~FactoryRegistrar()
{
assert( &isRegistered_ && isRegistered_ );
}
};
/////////////////////////////////////////////////////////////
// Initialize static member variable, whose real purpose
// is to register a with the factory as a side-effect
template<class Base, class Derived>
const bool FactoryRegistrar<Base,Derived>::isRegistered_
= FactoryRegistrar<Base,Derived>::MyFactory
::Instance().Register( Derived::Create );
/////////////////////////////////////////////////////////////
// Base class
struct B
{
virtual ~B() {}
virtual int GetInt() const { return -1; }
};
/////////////////////////////////////////////////////////////
// Derived class
struct D
: public B
, public FactoryRegistrar<B,D>
{
static B* Create() { return new D; }
int GetInt() const { return 42; }
};
/////////////////////////////////////////////////////////////
int main()
{
std::auto_ptr<B> pb( Factory<B,D>::Instance().Create() );
return pb->GetInt();
}
On the first two compilers mentioned, this works fine. On the last, it
asserts in Factory::Create().
Cheers! --M
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
|
| Back to top |
|
 |
Greg Herlihy Guest
|
Posted: Tue Nov 22, 2005 11:29 am Post subject: Re: Suboptimal optimization |
|
|
mlimber wrote:
| Quote: | kanze wrote:
mlimber wrote:
kanze wrote:
mlimber wrote:
....
2. Can anyone suggest a standard conformant way to force
instantiation of an inherited class like A<float> without
listing each instantiation explicitly? (Sneaky backdoors
accepted.)
Well, you're going to have to list the targetted types
somewhere; there are potentially an infinit number of types
over which the template can be instantiated, and the
compiler cannot instantiate them all. So some subset must
be specified, and the only way I know of for specifying such
a subset is to list all of its elements.
Right, but I would like to list the targeted types only in my
inheritance specifications, as in the OP.
So create a "side effect" in your inheritance specification
which triggers the instantiation of the static variable.
Alberto's suggestion of refering to the static variable in the
destructor, for example, will ensure instantiation of the static
variable any time the destructor is instantiated. And if the
class is used as a base class, the destructor *will* be
instantiated (since it is implicitly called by the destructor of
the derived class).
I have tried this and it works on VC++ 6 with sp6 and g++ 3.4.4, but
not with one essential element in my chain, viz. Texas Instruments C++
v.5.1.0, which I believe uses the EDG front-end. Is this a
bug/non-conformancy in their compiler? Here's the code that I used to
test it:
#include <cassert
#include
/////////////////////////////////////////////////////////////
// Factory class simplified from Loki::Factory
template<class Base, class Derived
class Factory
{
typedef Base* (*CreationFn)();
CreationFn creationFn_;
Factory() : creationFn_(0) {}
public:
static Factory& Instance() { static Factory f; return f; }
bool Register( const CreationFn creationFn )
{
creationFn_ = creationFn;
return true;
}
Base* Create() const
{
assert( creationFn_ != 0 );
return creationFn_();
}
};
/////////////////////////////////////////////////////////////
// This policy class automatically registers a Base/Derived
// pair with the appropriate factory.
template
class FactoryRegistrar
{
typedef Factory
static const bool isRegistered_;
protected:
virtual ~FactoryRegistrar()
{
assert( &isRegistered_ && isRegistered_ );
}
};
|
Remove the assert from FactoryRegistrar's destructor and run the
program again. The assertion in Factory::Create will fail because the
compiler no longer has a reason to instantiate FactoryRegistrar. In
other words it is only the presence of the assert that ensures
FactoryRegistrar's instantiation - an inherently fragile technique and
one suited only for debug builds at that. A much more reliable solution
would be to instruct the compiler to instantiate FactoryRegistrar
explicitly (see below).
| Quote: | /////////////////////////////////////////////////////////////
// Initialize static member variable, whose real purpose
// is to register a with the factory as a side-effect
template<class Base, class Derived
const bool FactoryRegistrar
= FactoryRegistrar<Base,Derived>::MyFactory
::Instance().Register( Derived::Create );
|
It is important to note that the above line does not instantiate
FactoryRegistrar<Base, Derived>::isRegistered. It merely instructs the
compiler how to initialize isRegistered should it be instantiated.
Clearly if the compiler has no cause to instantiate isRegistered, then
no call will be made FactoryRegistrar::Register() method in order to
initialize it.
A program that has a different set of expectations for these methods
and members will have to have a different set of declarations. For
example, if Register() must be called then it should be called to
initialize a non-template data member or global variable - either one
of which would be certain to exist. Alternately, if both Register must
be called and isRegistered must be instantiated, then inserting an
explicit template instantiation at this point would be in order:
template class FactoryRegistrar<Base, Derived>;
With this declaration FactoryRegistar<Base, Derived>- including all of
its methods and members - will be instantiated. There is no need to use
compiler-specific, obscure and error-prone methods to accomplish a task
that the language already specifically accomodates.
There is however an inherent contradiction in an explicit
instantiation. The entire reason for placing code in a template is to
make that code's presence in a program, conditional. It becomes part of
the program only if code already in the program needs it. Otherwise it
will be left out. An explicit instantiation adds the code to the
program whether or not it is needed by the rest of the program. So one
question raised is why package this code in a template if its presence
is required, or, alternately, why add the code to the program absent
any need for it?
Greg
[ 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
|
|