Re: Template function problem language or compiler bug?
On 7 Mai, 19:43, "Chris Morley" <chris.mor...@lineone.net> wrote:
I found a bit of a weirdo using Intel 4.5 compiler & I've seen similar bad
stuff in VC6. I don't know if it is a language 'feature' or a bug in the
compiler's template handling - the compilers are old.
Basically I have a template function (as a member in a class) with void
return & argument. To use the function you have to explicitly tell the
compiler what type - or the compiler errors (reasonable as it can't
determine the type!). The linked program then uses the first type
encountered for all links to that function (i.e. _same_ address) regardless
of they type provided! To fix it, if I declare a return type of the template
type instead of void, it links the explicitly defined function correctly! Is
this a bug with "template<class T> void fn(void) hadnling by this compiler
or a language feature in all C++ compilers? (feels like a name
mangling/linker bug to me)
Chris
e.g.
HEADER
class foo
{
public:
void SomeFN();
template<class T> void Broken();
template<class T> T Works();
OK, these are member functions.
};
template<class T> void Broken()
{
assert(sizeof(double) == sizeof(T)); // some code which uses T
This test has an implementation-defined result.
I know compilers, where sizeof(float) == sizeof(double),
which is legal, if other constraints are fulfilled.
}
template<class T> T Works()
{
assert(sizeof(double) == sizeof(T)); // some code which uses T
return 1;
}
And these are not.
void foo:SomeFN()
{
Broken<float>(); // ok, type is float in expanded fn
Broken<double>(); // broken, type is also float!
Works<float>(); // ok, type is float
Works<double>(); // ok, type is double
}
Note that above program will *never* attempt to invoke
the above defined *free* functions Broken and Works.
Because your program has no definitions of foo::Broken
and foo::Works it violates the ODR, which induces
undefined behavior. I'm rather sure, that this was not
the actual tested program, but we could legally stop
here with the analysis ;-)
Because this would be a bit too simple ;-), let's first
transform it into a syntactically valid program which
obeys the ODR where we use a safe test and where we also
ensure that the test happens during compile-time, which
eases the diagnostics:
template<typename T>
struct is_double {
static const bool value = false;
};
template<>
struct is_double<double> {
static const bool value = true;
};
class foo
{
public:
void SomeFN();
template<class T> void Broken();
template<class T> T Works();
};
template<class T> void foo::Broken()
{
typedef bool test[is_double<T>::value ? 1 : -1];
}
template<class T> T foo::Works()
{
typedef bool test[is_double<T>::value ? 1 : -1];
return 1;
}
void foo::SomeFN()
{
Broken<float>(); // Should fail to compile
Broken<double>(); // Should be accepted
Works<float>(); // Should fail to compile
Works<double>(); // Should be accepted
}
int main() {
foo().SomeFN();
}
Does this program fail to compile? If not, then
this is a compiler defect. And this defect is probably
an optimization error, because it is well-known, that
some compilers 'merge' different templates instantiations
into one entity. If this is done, without causing
observable behaviour, this is indeed a valid
technique. E.g. if your actual definitions of foo::Works
and foo::Broken would have been written this
way:
#include <iostream>
#include <ostream>
template<class T> void foo::Broken()
{
std::cout << "My name is Broken" << std::endl;
}
template<class T> T foo::Works()
{
std::cout << "My name is Works" << std::endl;
return 1;
}
than the compiler could perform this technique,
because you could not observe the difference with
your program above. *But* if you have used your
assert test with my is_double predicate *and* if
you have *not* defined NDEBUG, then you have
a program, which leads to observable behaviour
according to the standard, which can be deduced
from the following quotations from the current C++
standard:
[intro.execution]/6:
"The observable behavior of the abstract machine is
its sequence of reads and writes to volatile data and
calls to library I/O functions."
and the C standard (unfortunately I have only the
C99 standard available):
7.2.1.1/2: (The assert macro)
"[..] When it is executed, if expression [..] is false [..],
the assert macro writes information about the particular call
that failed [..] on the standard error stream in an
implementation-defined format. It then calls the abort function."
So, if the program compiles and runs successfully,
it violates the C++ standard.
HTH & Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]