Compiler Error in Template Functions...? [modified]
-
I have run into a very strange error today and was wondering if anyone else has seen this. Some digging on google suggests it might be a compiler error. I've seen this on VS2008 and GCC 4.1.2. I was able to build a test harness to repro the issue. It is below:
#include class Cloneable
{
public:
template TYPE* clone() const { return dynamic_cast< TYPE* > ( clone() ); }
virtual Cloneable* clone() const = 0;
};// simple template class that defines an interface
// for cloning objects.
class X : public Cloneable
{
public:
X(int _y ) : data(_y) { }
virtual ~X() { /* DO NOTHING */ }
virtual Cloneable* clone() const { return new X(data); }protected:
X() { /* DO NOTHING */ }
int data;
};class Y : public X
{
public:
Y(double f, int y) : X(y), dataf(f) { /* DO NOTHING */ }
virtual ~Y() { /* DO NOTHING */ }virtual Cloneable\* clone() const { return new Y(dataf, data); }
protected:
double dataf;
};// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
int main(int argc, char* argv[])
{
X* x = new X(1);
X* x1 = x->clone();Y\* y = new Y(2.12, 4); Y\* y1 = y->clone(); delete y; delete y1; delete x; delete x1; return 0;
}
The clone fails in both cases in the main function. It should cast properly. There seem to be two ways to fix this. If i move the templated function from Cloneable to X & Y it works in both cases. The other is to use clone() in the main function and dyna cast there. A standard C cast in the templated function also doesn't work. Its like the compiler is erroneously changing the protection level on the function and then gets confused and just dumps out the error.
1>Compiling...
1>main.cpp
1>.\main.cpp(40) : error C2275: 'X' : illegal use of this type as an expression
1> .\main.cpp(12) : see declaration of 'X'
1>.\main.cpp(40) : error C2059: syntax error : ')'
1>.\main.cpp(43) : error C2275: 'Y' : illegal use of this type as an expression
1> .\main.cpp(24) : see declaration of 'Y'
1>.\main.cpp(43) : error C2059: syntax error : ')'Anyone seen this work in a different compiler or know of a workaround to make it compile (aside from the ones I mentioned)? -- Joseph Dempsey Sr. Software Engineer joseph_r_dempsey@yahoo.com
modified on Friday, November 5, 2010 2:18 PM
-
I have run into a very strange error today and was wondering if anyone else has seen this. Some digging on google suggests it might be a compiler error. I've seen this on VS2008 and GCC 4.1.2. I was able to build a test harness to repro the issue. It is below:
#include class Cloneable
{
public:
template TYPE* clone() const { return dynamic_cast< TYPE* > ( clone() ); }
virtual Cloneable* clone() const = 0;
};// simple template class that defines an interface
// for cloning objects.
class X : public Cloneable
{
public:
X(int _y ) : data(_y) { }
virtual ~X() { /* DO NOTHING */ }
virtual Cloneable* clone() const { return new X(data); }protected:
X() { /* DO NOTHING */ }
int data;
};class Y : public X
{
public:
Y(double f, int y) : X(y), dataf(f) { /* DO NOTHING */ }
virtual ~Y() { /* DO NOTHING */ }virtual Cloneable\* clone() const { return new Y(dataf, data); }
protected:
double dataf;
};// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
int main(int argc, char* argv[])
{
X* x = new X(1);
X* x1 = x->clone();Y\* y = new Y(2.12, 4); Y\* y1 = y->clone(); delete y; delete y1; delete x; delete x1; return 0;
}
The clone fails in both cases in the main function. It should cast properly. There seem to be two ways to fix this. If i move the templated function from Cloneable to X & Y it works in both cases. The other is to use clone() in the main function and dyna cast there. A standard C cast in the templated function also doesn't work. Its like the compiler is erroneously changing the protection level on the function and then gets confused and just dumps out the error.
1>Compiling...
1>main.cpp
1>.\main.cpp(40) : error C2275: 'X' : illegal use of this type as an expression
1> .\main.cpp(12) : see declaration of 'X'
1>.\main.cpp(40) : error C2059: syntax error : ')'
1>.\main.cpp(43) : error C2275: 'Y' : illegal use of this type as an expression
1> .\main.cpp(24) : see declaration of 'Y'
1>.\main.cpp(43) : error C2059: syntax error : ')'Anyone seen this work in a different compiler or know of a workaround to make it compile (aside from the ones I mentioned)? -- Joseph Dempsey Sr. Software Engineer joseph_r_dempsey@yahoo.com
modified on Friday, November 5, 2010 2:18 PM
You're code is not fully visible. Make sure "Encode < characters while pasting" option is checked while you paste your code.
«_Superman_» _I love work. It gives me something to do between weekends.
-
You're code is not fully visible. Make sure "Encode < characters while pasting" option is checked while you paste your code.
«_Superman_» _I love work. It gives me something to do between weekends.
It was already checked. It shows up fully in my browser (chrome) and in IE just fine. Not sure why you can't see it.
-- Joseph Dempsey Sr. Software Engineer joseph_r_dempsey@yahoo.com
-
It was already checked. It shows up fully in my browser (chrome) and in IE just fine. Not sure why you can't see it.
-- Joseph Dempsey Sr. Software Engineer joseph_r_dempsey@yahoo.com
I'm using chrome and this is what I see -
#include
class Cloneable
{
public:
template TYPE* clone() const { return dynamic_cast< TYPE* > ( clone() ); }
virtual Cloneable* clone() const = 0;
};// simple template class that defines an interface
// for cloning objects.
class X : public Cloneable
{
public:
X(int _y ) : data(_y) { }
virtual ~X() { /* DO NOTHING */ }
virtual Cloneable* clone() const { return new X(data); }protected:
X() { /* DO NOTHING */ }
int data;
};class Y : public X
{
public:
Y(double f, int y) : X(y), dataf(f) { /* DO NOTHING */ }
virtual ~Y() { /* DO NOTHING */ }virtual Cloneable\* clone() const { return new Y(dataf, data); }
protected:
double dataf;
};// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
int main(int argc, char* argv[])
{
X* x = new X(1);
X* x1 = x->clone();Y\* y = new Y(2.12, 4); Y\* y1 = y->clone(); delete y; delete y1; delete x; delete x1; return 0;
}
«_Superman_» _I love work. It gives me something to do between weekends.
-
I'm using chrome and this is what I see -
#include
class Cloneable
{
public:
template TYPE* clone() const { return dynamic_cast< TYPE* > ( clone() ); }
virtual Cloneable* clone() const = 0;
};// simple template class that defines an interface
// for cloning objects.
class X : public Cloneable
{
public:
X(int _y ) : data(_y) { }
virtual ~X() { /* DO NOTHING */ }
virtual Cloneable* clone() const { return new X(data); }protected:
X() { /* DO NOTHING */ }
int data;
};class Y : public X
{
public:
Y(double f, int y) : X(y), dataf(f) { /* DO NOTHING */ }
virtual ~Y() { /* DO NOTHING */ }virtual Cloneable\* clone() const { return new Y(dataf, data); }
protected:
double dataf;
};// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
int main(int argc, char* argv[])
{
X* x = new X(1);
X* x1 = x->clone();Y\* y = new Y(2.12, 4); Y\* y1 = y->clone(); delete y; delete y1; delete x; delete x1; return 0;
}
«_Superman_» _I love work. It gives me something to do between weekends.
ok... not sure whats going on there but i tried repasting it with and w/o the checkbox and it still shows up erred. Its missing the angle brackets obviously. The code itself is correct syntactically. Just pretend they are there :) If you're real interested in seeing the actual file i can email it. * EDIT: Screw it... just removed HTML completely. should show up right now.
-- Joseph Dempsey Sr. Software Engineer joseph_r_dempsey@yahoo.com
-
ok... not sure whats going on there but i tried repasting it with and w/o the checkbox and it still shows up erred. Its missing the angle brackets obviously. The code itself is correct syntactically. Just pretend they are there :) If you're real interested in seeing the actual file i can email it. * EDIT: Screw it... just removed HTML completely. should show up right now.
-- Joseph Dempsey Sr. Software Engineer joseph_r_dempsey@yahoo.com
You're trying to overload across class boundaries. Take a look at this FAQ - http://www.research.att.com/~bs/bs_faq2.html#overloadderived[^] So you will need the add the following line in your derived classes -
using Cloneable::clone;
«_Superman_»
_I love work. It gives me something to do between weekends.
-
I have run into a very strange error today and was wondering if anyone else has seen this. Some digging on google suggests it might be a compiler error. I've seen this on VS2008 and GCC 4.1.2. I was able to build a test harness to repro the issue. It is below:
#include class Cloneable
{
public:
template TYPE* clone() const { return dynamic_cast< TYPE* > ( clone() ); }
virtual Cloneable* clone() const = 0;
};// simple template class that defines an interface
// for cloning objects.
class X : public Cloneable
{
public:
X(int _y ) : data(_y) { }
virtual ~X() { /* DO NOTHING */ }
virtual Cloneable* clone() const { return new X(data); }protected:
X() { /* DO NOTHING */ }
int data;
};class Y : public X
{
public:
Y(double f, int y) : X(y), dataf(f) { /* DO NOTHING */ }
virtual ~Y() { /* DO NOTHING */ }virtual Cloneable\* clone() const { return new Y(dataf, data); }
protected:
double dataf;
};// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
int main(int argc, char* argv[])
{
X* x = new X(1);
X* x1 = x->clone();Y\* y = new Y(2.12, 4); Y\* y1 = y->clone(); delete y; delete y1; delete x; delete x1; return 0;
}
The clone fails in both cases in the main function. It should cast properly. There seem to be two ways to fix this. If i move the templated function from Cloneable to X & Y it works in both cases. The other is to use clone() in the main function and dyna cast there. A standard C cast in the templated function also doesn't work. Its like the compiler is erroneously changing the protection level on the function and then gets confused and just dumps out the error.
1>Compiling...
1>main.cpp
1>.\main.cpp(40) : error C2275: 'X' : illegal use of this type as an expression
1> .\main.cpp(12) : see declaration of 'X'
1>.\main.cpp(40) : error C2059: syntax error : ')'
1>.\main.cpp(43) : error C2275: 'Y' : illegal use of this type as an expression
1> .\main.cpp(24) : see declaration of 'Y'
1>.\main.cpp(43) : error C2059: syntax error : ')'Anyone seen this work in a different compiler or know of a workaround to make it compile (aside from the ones I mentioned)? -- Joseph Dempsey Sr. Software Engineer joseph_r_dempsey@yahoo.com
modified on Friday, November 5, 2010 2:18 PM
Have a dig in the standard for the rules on overload resolution and how it interacts with template expansion. As both VC++ later than 2002 and gcc 4.x agree on the behaviour it's probably standard. Essentially it looks like what's happening is that clone<T> is not part of the overload set for Y::clone() and X::clone() as there functions with that name already in the class of the pointer you're calling through. One of three solutions spring to mind. Either make clone an ordinary function, change it's name or call it explicitly. e.g. if you change your base class to:
class Cloneable
{
public:
template<class TYPE> TYPE* clone_as() const { return dynamic_cast< TYPE* > ( clone() ); }
virtual Cloneable* clone() const = 0;
};and change your client code to match things work the way I think you think they should. Incidentally be very careful writing code like this... You can write leaky code very easily:
int main()
{
X x( 7 );
Cloneable *p = &x;Y \*py = p->clone\_as<Y>();
}
leaks an X. I'd be tempted to return shared_ptrs or auto_ptrs to avoid this sort of faux pas. Basically the interaction between overloading, overriding and templates (i.e. when templates are expanded and considered part of the set of overload candidates) is a nightmare. My advice would be avoid it unless you're writing a compiler. Cheers, Ash PS: This is very similar to the well known interaction between overrides and overloads with implicit conversion:
class base
{
public:
virtual void do_something( int ) { std::cout << "Doing the base class int thing" << std::endl; }
virtual void do_something( double ) { std::cout << "Doing the base class double thing" << std::endl; }
};class derived : public base
{
public:
virtual void do_something( double ) { std::cout << "Doing the derived class double thing" << std::endl; }
};int main()
{
derived d;
d.do_something( int( 7 ) );
}This could always prints the derived class double message. You get around this by either changing the name or calling the function explicitly. PPS: Superman's solution from the mouth of Bjarne would work as well - I didn't consider it as using declarations have their own fun properties which you may or may not be happy with.
modified on Saturday, November 6, 2010 2:48 AM
-
You're trying to overload across class boundaries. Take a look at this FAQ - http://www.research.att.com/~bs/bs_faq2.html#overloadderived[^] So you will need the add the following line in your derived classes -
using Cloneable::clone;
«_Superman_»
_I love work. It gives me something to do between weekends.
-
I have run into a very strange error today and was wondering if anyone else has seen this. Some digging on google suggests it might be a compiler error. I've seen this on VS2008 and GCC 4.1.2. I was able to build a test harness to repro the issue. It is below:
#include class Cloneable
{
public:
template TYPE* clone() const { return dynamic_cast< TYPE* > ( clone() ); }
virtual Cloneable* clone() const = 0;
};// simple template class that defines an interface
// for cloning objects.
class X : public Cloneable
{
public:
X(int _y ) : data(_y) { }
virtual ~X() { /* DO NOTHING */ }
virtual Cloneable* clone() const { return new X(data); }protected:
X() { /* DO NOTHING */ }
int data;
};class Y : public X
{
public:
Y(double f, int y) : X(y), dataf(f) { /* DO NOTHING */ }
virtual ~Y() { /* DO NOTHING */ }virtual Cloneable\* clone() const { return new Y(dataf, data); }
protected:
double dataf;
};// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
int main(int argc, char* argv[])
{
X* x = new X(1);
X* x1 = x->clone();Y\* y = new Y(2.12, 4); Y\* y1 = y->clone(); delete y; delete y1; delete x; delete x1; return 0;
}
The clone fails in both cases in the main function. It should cast properly. There seem to be two ways to fix this. If i move the templated function from Cloneable to X & Y it works in both cases. The other is to use clone() in the main function and dyna cast there. A standard C cast in the templated function also doesn't work. Its like the compiler is erroneously changing the protection level on the function and then gets confused and just dumps out the error.
1>Compiling...
1>main.cpp
1>.\main.cpp(40) : error C2275: 'X' : illegal use of this type as an expression
1> .\main.cpp(12) : see declaration of 'X'
1>.\main.cpp(40) : error C2059: syntax error : ')'
1>.\main.cpp(43) : error C2275: 'Y' : illegal use of this type as an expression
1> .\main.cpp(24) : see declaration of 'Y'
1>.\main.cpp(43) : error C2059: syntax error : ')'Anyone seen this work in a different compiler or know of a workaround to make it compile (aside from the ones I mentioned)? -- Joseph Dempsey Sr. Software Engineer joseph_r_dempsey@yahoo.com
modified on Friday, November 5, 2010 2:18 PM
I wouldn't recommend to do it that way at all.. Let
Clonable
be what it is, an interface with no implementation, skip the template member completely. In your example you don't need to cast anything, if you use covariant return type for overrides ofclone
inimplementations ofClonable
:class X : public Cloneable
{
virtual X* clone() const { return new X(data); }
};class Y : public X
{
virtual Y* clone() const { return new Y(dataf, data); }
};int main(int argc, char* argv[])
{
X* x = new X(1);
X* x1 = x->clone();Y\* y = new Y(2.12, 4); Y\* y1 = y->clone(); return 0;
}
As suggested above, usage of an
std::auto_ptr
is preferable, but only in client code, since otherwise you loose the ability to define covariant return types. I am using a "Cloner" utility for comfortable casts to derived types which looks something like this:class ClonerUtility {
template <class TCoVariantClonable>
static std::auto_ptr<TCoVariantClonable> CloneAs(const Clonable& pToBeCloned) {
std::auto_ptr<Clonable> tClone(pToBeCloned.clone());
if (TCoVariantClonable* tCoVariantClone =
dynamic_cast<TCoVariantClonable*>(tClone.get())) {
tClone.reset(0);
return std::auto_ptr<TCoVariantClonable>(tCoVariantClone);
}
return std::auto_ptr<TCoVariantClonable>();
}template<class TCoVariantClonable>
static std::auto_ptr<TCoVariantClonable> Clone(const TCoVariantClonable& pToBeCloned) {
return std::auto_ptr<TCoVariantClonable>(pToBeCloned.clone());
}
};//
int main(int argc, char* argv[])
{
std::auto_ptr<X> tX(new X);
std::auto_ptr<X> tCloneX = ClonerUtility::Clone(*tX);Clonable& tPureClonable = *tCloneX;
std::auto_ptr<X> tOtherCloneOfX = ClonerUtility::CloneAs<X>(tPureClonable);
}modified on Sunday, November 7, 2010 4:05 AM