Jump to content
  • Advertisement
Sign in to follow this  
spraff

Quick templates question

This topic is 3416 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello. Here's my problem distilled:
template <int i, typename T>
class Foo {
public:
	enum Enum {A,B,C};
	void bar (Enum);
};

template <typename T>
void Foo<0,T>::bar (typename Foo<0,T>::Enum) { // Error here
}

This doesn't compile -- "invalid use of incomplete type class Foo<0, T>" -- but I don't understand why. Thanks if you can explain this...

Share this post


Link to post
Share on other sites
Advertisement
spraff,

You can't partially specialize a member function of a templatized class. You must partially specialize the class template instead. Check the code below, it should compile:


template <int i, typename T>
class Foo {
public:
enum Enum {A,B,C};
void bar (Enum);
};

template <typename T>
class Foo<0,T> {
public:
enum Enum {A,B,C};
void bar (Enum);
};

template <typename T>
void Foo<0,T>::bar ( Foo<0,T>::Enum) { // Error here
}




It sort of sucks, but that's the way things work (for now).

Cheers,
Rob

Share this post


Link to post
Share on other sites
You can get around some of this as follows:

class FooBase {
public:
enum Enum {A,B,C};
};
template <int i, typename T>
class Foo: public FooBase {
void bar (Enum);
};

template <typename T>
class Foo<0,T>: public FooBase {
public:
void bar (Enum);
};

template <typename T>
void Foo<0,T>::bar ( Foo<0,T>::Enum) {
}


Ie, factor out the common parts of Foo and move it to FooBase.

You can also do a jump-through-a-hoop trick by having bar redirect to a (possibly private) template functor being passed the template parameters of Foo, and do partial template specialisation on the bar functor class to catch the zero case and deal with it differently.

But really, do you want to?

Share this post


Link to post
Share on other sites
Thanks for clearing that up.

I want to avoid following your solution exactly since my actual code is considerably more complex, so I want to exploit inheritence to tidy things up. Here's what I'm trying to work with now:
// X and Y show expected inheritence of Enum.
class X {
public:
enum Enum {a,b};
};

class Y : public X {
public:
void foo (Enum); // #1
};

// "Base" class is very large, don't want to
// duplicate it, "Enum" represents all that.
template <int i, typename T>
class Base {
public:
enum Enum {A,B,C};
};

// Except for special values of I, Foo is
// equivalent to Base.
template <int I, typename T>
class Foo : public Base<I,T> {
public:
};

// Special behaviour for specific I.
// Note that line #2 should behave as #1
// but instead it generates an error:
// "Enum has not been declared"
template <typename T>
class Foo<0,T> : public Base<0,T> {
public:
void bar (Enum); // #2
};

template <typename T>
class Foo<1,T> : public Base<0,T> {
public:
void bar (Enum, int);
};

template <typename T>
void Foo<0,T> :: bar (typename Foo<0,T>::Enum) {
std::cout << "Foo<0,>\n";
}

template <typename T>
void Foo<1,T> :: bar (typename Foo<1,T>::Enum,int) {
std::cout << "Foo<1,>\n";
}

typedef Foo<0,float> A;
typedef Foo<1,float> B;

int main () {
A a;
B b;
a.bar(A::C);
b.bar(B::C,0);
return 0;
}

Can you explain why Base::Enum isn't visible to Foo?

Share this post


Link to post
Share on other sites
As a template-argument dependant type, prior to knowing the template arguments your code cannot determine if it is a type or not. Nor can it determine if you are referring to a symbol defined in your parent, or a global symbol, at the time that the template is parsed.

You tell the compiler that something is actually a type using the typename keyword.

You tell the compiler that your symbol refers to something in your parent by naming your parent explicitly.


template<typename T>
struct Base {
struct foo {};
};

typedef enum { one = 1 } foo;

template<typename T>
struct Child: Base<T> {
typedef Base<T> parent;
void bar( typename parent::foo );
typedef typename parent::foo foo;
void baz( foo );
};

Base is a template class. It defines a type Base<T>::foo.

Child inherits from Base<T>. I do a typedef of Base<T> to parent to get a shorter, less cumbersome name for the class I inherited from (in your case, this is more important, as you have multiple parameters).

When I wrote the bar function, I fully qualified the foo type so that the parser could determine that I am referring to something from my parent, and not the foo that is visible at global scope.

I had to use typename, because as far as the compiler knows, the symbol parent::foo could be an enum. Ie, some ass later could write:

template<>
struct Base<int> {
enum { foo = 7 };
};

at the time that I actually instantiate the Child template. In general, parent::foo being an enum or a static or a method or an enum value is ambiguous from context, and can change what the parse tree looks like for the template. So I have to provide the compiler with the hint that it is a typename.

Tired of typing out typename all of the time, I simply use a typedef to bring the foo name into my own scope, and can now use foo without problem.

Share this post


Link to post
Share on other sites
Ah, thanks a lot. This is making more sense now. There's one more detail that needs to be ironed out: default function arguments. The "Enum" enumeration is defined in the template base class, and referring to Enum requires "typename Base<foo>::Enum".

That's for when you need to tell the compiler that a symbol is a type. What about when you need to tell the compiler that a symbol is NOT a type?

template <typename T>
class Foo<0,T> : public Base<0,T> {
public:
typedef typename Base<0,T>::Enum Enum;
void bar (Enum=B); // B is not defined.
};


A similar error, but the opposite problem? What's the solution this time, O wise ones? :-)

Share this post


Link to post
Share on other sites
Quote:
Original post by magic_man
void bar (Enum=B);

Are you trying to give it a default value here as that line does not make any sense.

Yes. Perhaps I should have re-posted an earlier snippet:
template <int i, typename T>
class Base {
public:
enum Enum {A,B,C};
};




[Edited by - spraff on March 13, 2009 8:12:07 AM]

Share this post


Link to post
Share on other sites
I'm going to use the fish trick, rather than answering the last question.

struct foo {
enum stuff {A, B, C};
};

void bar( stuff = B );

Make bar compile.

Share this post


Link to post
Share on other sites
Quote:
Original post by NotAYakk
I'm going to use the fish trick, rather than answering the last question.

struct foo {
enum stuff {A, B, C};
};

void bar( stuff = B );

Make bar compile.

Shouldn't that be "void bar (foo::stuff = foo::B)"? I don't see how that's relevant.

To recap:

template <int i, typename T>
class Base {
public:
enum Enum {A,B,C};
};

template <typename T>
class Foo<0,T> : public Base<0,T> {
public:
typedef typename Base<0,T>::Enum Enum;
void bar (Enum=B); // B is not defined.
// Here I had to refer to "typename Base<0,T>::Enum" to
// get the Enum symbol recognised. But how do I make B recognised?
// Surely Foo::B is defined since Foo::Enum is defined?
};

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!