Problem With Class To Template Convertion! Confused..

Started by
7 comments, last by Deyja 17 years, 6 months ago
Hello there! I have a Class like this:
namespace BEK
{
  class myClass
  {
    enum MyEnum
    {
      ITEM1,
      ITEM2
    }
    
    static MyEnum enum1;
}

and at the cpp file i have this:

namespace BEK
{
  MyClass::MyEnum MyClass::enum1;
}
When I try to Convert it to template like this:
namespace BEK
{
  template<classT>
  class myClass
  {
    enum MyEnum
    {
      ITEM1,
      ITEM2
    }
    
    static MyEnum enum1;
}

namespace BEK
{
  template<class T> MyClass<T>::MyEnum template<class T> MyClass<T>::enum1;
}
I get this errors/warnings: warning C4346: 'BEK::myClass<T>::MyEnum' : dependent name is not a type error C2143: syntax error : missing ';' before ''template<'' error C4430: missing type specifier - int assumed. Note: C++ does not support default-int error C2888: 'MyClass<T>::MyEnum MyEnum' : symbol cannot be defined within namespace 'BEK' I am a little confuzed :S Can somebody explain me what is going on? thanks for your time! [Edited by - Fruny on September 29, 2006 1:18:01 PM]
Advertisement
Little tip: MSs compiler errors/warnings are so well documented, you can google for them and the first link is almost always MSs own page about the error/warning in question. Alternatively, you can select the error in question and simply press F1. VS is really an impressive piece of work.

In brief, any time you have a templated type, you need to add typename to access subtypes:
template<typename T> foo {   typedef int bar;};//laterfoo<int>::bar x; //C4346typename foo<int>::bar x; //fine


That ensures the compiler that bar is a type, and not a static member of some sort. In this case, it looks pretty obvious. But sometimes it isn't so clear cut:
template<typename T> foo {   typedef int bar;};template<> foo<int> {   static const int bar = 15; //zomg!};//laterfoo<int>::bar x; //foo<int>::bar is a const int, not a typefoo<float>::bar y; //foo<float>::bar is a type, so this would be OK if it didn't need typename added

CM
Templates and static members don't mix particularly well. Template code is only created when an instance of the template type is declared:
template <class T>class Thing{public:    T thing;};// the above template declaration does not create any code at allThing <int>    an_instance_of_a_thing;// the above instatiates the template and creates the code for the instance(*)

So, if you had a static member in the template, the declaration:
Thing <typename>::static_member_type    Thing <typename>::static_member;

doesn't create anything since you've not specified a template parameter. To create the code, you need to provide a template parameter:
Thing <int>::static_member_type    Thing <int>::static_member;

But that doesn't really help because you'd have to write that line for each type the template could be instantiated with, which defeats some of the ideas behind templates. As a result of that:
Thing <int>::static_memberThing <float>::static_member

are two different variables, assigning to one will not affect the other.

So, do you want a single static member variable shared accross all types or will manual instantiation of the member be OK (i.e. a separate one for each type)?

Skizz

(*) As a result of this, instantiating two instances of a template produces twice as much code as instantiating one instance of a template.(**)
(**) Although it does depend on the type used to instantiate the template.(***)
(***) But modern compilers might be able to do some smart stuff to reduce this.
Storage of static members in templates sucks.

So make the compiler do it for you.

template<typename whatever>class example_template {  private:    static int& example_static() {      int storage = 0;      return storage;    }  // ... more code};


By using a static member function of the template class, the compiler will automatically allocate storage for your static member for each seperate template type instantiated.

Now, this isn't often what you want. Often you want:
class example_base {  static int& example_static() {    int storage = 0;    return storage;  }};template<typename whatever>class example_template: public example_base {  // ... more code};


The difference is that in the first case, there is one copy of the int for each type "whatever" you create the template with, and in the second case there is one copy of the int that every template instance shares.

I avoid using static members -- I find that using a static function works better, not the least because storage is more natural, and because I can later easily add access guards to the static data.
Hmm, how about templating the example_base as well? :)

template <typename I, I n> // is that OK? o_Ostruct static_value {  static I& operator()() {    I storage = n;    return storage;  }   // I doubt these would ever be useful, but:  template <typename I1, I1 n>  static_value(const static_value<I1, n>& value) {    (*this)() = static_cast(value());  }  template <typename I1, I1 n>  static_value& operator=(const static_value<I1, n>& value) {    (*this)() = static_cast(value());    return *this;  }};// And a const-overload of sorts:template <typename I, I n>struct immutable_static_value {  static const I& operator()() const {    I storage = n;    return storage;  }  // Obviously can't have/don't want the other operations here.};struct foo: static_value<short, 4> {};struct bar: static_value<short, 4> {};template<typename whatever>class example_template: public foo, public bar { // maybe private instead? :s  whatever x;  public:  static void wibble() {    std::cout << x << ++foo() << bar() << std::endl;  }};


[Edited by - Zahlman on September 30, 2006 5:12:47 AM]
Because the name of the static data is lost, Zahlman, and your code only works for intergral values (doesn't work for doubles, strings, and anything that isn't essentially an int).

Second, static operator() .. that is at the very least impolite use of operators, and practically ugly. You should override operators when the operation is analagous.

Third, your syntax looks like you are creating a foo and a bar, not calling static operator().

Quote:Original post by NotAYakk
Because the name of the static data is lost, Zahlman


It's supposed to be supplied by the typedef-name of the class you instantiate. It does have to come from somewhere; I suppose creating the typedef is hardly any less boilerplate than rewriting the struct every time. :(

Quote:and your code only works for intergral values (doesn't work for doubles, strings, and anything that isn't essentially an int).


True, as I realized while constructing the usage example :)

Quote:Second, static operator() .. that is at the very least impolite use of operators, and practically ugly. You should override operators when the operation is analagous.


I was following the previous version a little too closely, I think. 'static' should apply not to the operator, but to 'storage', for this one.
Thanks for your help guys!! really thanks!
I will find my way now :D
You guys need to proof read your code. Not you, zahlman. You're perfect as always.

template <typename T>class static_member{public:   static T& value()    {      static T v;      return v;   }};template <typename T>class example : public static_member<int>{}int main(){    example<int> foo;    example<float> bar;    foo.value() = 42;    assert(bar.value() == foo.value());}


If you need more than one static with the same type, you'll have to write wrappers in the child template, and differentiate between base members somehow.

This topic is closed to new replies.

Advertisement