Re: Template function problem language or compiler bug?
On 8 Mai, 17:13, "Chris Morley" <chris.mor...@lineone.net> wrote:
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.
Yeah, I know, I snipped the real code which is dependent on the type & then
dispatches another type dependent function. I spotted too that I missed out
the foo: from the Works/Broken defns after I pushed send... this is a more
representitive works/broken
template<class T> void foo::GetBasic()
{
assert(sz == sizeof(T)); // sz is known, it comes from input byte sequence
T bas;
/* snip - code fills bas */
pSomeClass->Element(tag, bas);
}
I spotted the problem first because I triggered the assert. Then noticed it
called the wrong Element() for the type. Then noticed that all GetBasic were
fixed up to an identical address.
OK.
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
As you say, not the tested program but it shouldn't be undefined (should
it?). The accidental global functions would be ignored & I'd get 2x link
errors as it can't link the foo: versions. As I have "reused" the same fn
names in foo as the globals, foo names trump globals so it would only try to
link if I used ::Works() or ::Broken(). Not undefined or am I missing your
point?
The one-definition-rule is supposed to mean the "exactly-one-
definition-for-each-used-entity"-rule ;-) So, if an entity
(like one of your functions) is *used* (and the member
functions in your example *were* used), then you are already
violating the ODR. A linker error is one possible (and the
most typical) symptom for such violation. This is so, because
the C++ does not specify the details of a linker, nor it's
diagnostics (if this exists at all). So, according to the
standard such a missing definition leads to undefined
behavior.
Does this program fail to compile? If not, then
Indeed, (VC6 won't build it at all because you can't define a constant in a
class defn, it thinks = is always a pure virtual fn token!) Intel 4.5 gives
the errors you intend, namely array size must be >0 in the 2 places.
Below is a complete program that exhibits my behaviour, it won't compile
with VC6 (internal compiler error) but will with intel 4.5. The type
function can equally be global or in another class with the same output.
Thanks,
Chris
--------------------------------
#include <iostream>
#include <ostream>
class foo
{
public:
void SomeFN();
template<class T> void Broken();
template<class T> T Works();
template<class T> void Works2(T* dummy);
template<class T> const char* Type(T* dummy);
};
template<class T> void foo::Broken()
{
std::cout << "My name is Broken : " << Type((T*) NULL) << std::endl;
}
template<class T> T foo::Works()
{
std::cout << "My name is Works : " << Type((T*) NULL) << std::endl;
return 1;
}
template<class T> void foo::Works2(T* dummy)
{
std::cout << "My name is Works2 : " << Type((T*) NULL) << std::endl;
}
template<> const char* foo::Type<double>(double* dummy)
{
return "double";
}
template<> const char* foo::Type<float>(float* dummy)
{
return "float";
}
void foo::SomeFN()
{
Broken<float>();
Broken<double>();
Works<float>();
Works<double>();
Works2<float>(NULL);
Works2<double>(NULL);
}
int main() {
foo().SomeFN();
}
OK, this is a valid test program and it causes
observable behavior according to the standard.
With output (debug & optimizations on):
My name is Broken : float
My name is Broken : float
Yeah, this one is definitively an compiler/optimization
error. And yes, it belongs to one of the quirks of
the VC6 compiler (and seemingly the Intel compiler as
well). If you study Boost code that still supports VC6
(until 1.34.1, if I remember correctly), you will quite
often find code like this as a workaround:
#include <boost/type.hpp>
#if defined(BOOST_MSVC) && BOOST_MSVC <= 1200 // 1200 = VC6
# define BOOST_EXPLICIT_DEFAULT_TARGET , ::boost::type<Target>* = 0
#else
# define BOOST_EXPLICIT_DEFAULT_TARGET
#endif
template <class Target, class Source>
inline Target polymorphic_cast(Source* x
BOOST_EXPLICIT_DEFAULT_TARGET) {
...
}
Here boost::type is just the following simple
class template:
namespace boost {
template <class T>
struct type {};
}
A similar approach should help you make your
function templates more robust:
template <class T>
struct wrapper{};
template<class T> void foo::Broken(wrapper<T>* = 0)
{
....
}
I strongly propose that you add such dummy parameter
for all function template argument that cannot be
deduced by arguments, even though some of your examples
seem to work for the moment.
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! ]