开发者

typename when defining map data that is a function pointer with a sprinkling of templates

This is a strange question because I already know the 'coding' answer. I just want to get a better understanding of why it is so. There are guru's here who have a knack of explaining these things better than the C++ standard :)

Below we have a means to define an abstract factory template that allocates objects based on a string as a key (it is a contrived example):-

#include <iostream>
#include <map>
#include <string>

using namespace std;

template <typename T, typename TProduct>
TProduct *MyFactoryConstructHelper(const T *t)
{
  if (!t) return new T;
  return new T(*static_cast<const T*>(t));
}

template <typename TProduct>
class AbstractFactory
{
public:
  typedef TProduct *(*MyFactoryConstructor)(const void *);
  typedef map<string, MyFactoryConstructor> MyFactoryConstructorMap;

  static TProduct *Create(const string &iName)
  {
    MyFactoryConstructor ctr = mTypes[iName];
    TProduct *result = NULL;
    if(ctr) result = ctr(NULL);
    return result;
  }

  template <typename T>
  static bool Register(const string &iName) {
    typedef TProduct*(*ConstructPtr)(const T*);
    ConstructPtr cPtr = MyFactoryConstructHelper<T, TProduct>;
    string name = iName;
    mTypes.insert(pair<string,MyFactoryConstructor>(name, reinterpret_cast<MyFactoryConstructor>(cPtr)));
    return(true);
  }

protected:
  AbstractFactory() {}
  static MyFactoryConstructorMap mTypes;
};

template <typename TProduct>
m开发者_如何学编程ap<string, /*typename*/ AbstractFactory<TProduct>::MyFactoryConstructor> AbstractFactory<TProduct>::mTypes;

Here is an example of how we use it: -

class MyProduct
{
public:
  virtual ~MyProduct() {}

  virtual void Iam() = 0;
};

class MyProductFactory : public AbstractFactory<MyProduct> 
{
public:
};

class ProductA : public MyProduct
{
public:
  void Iam() { cout << "ProductA" << endl; }
};

class ProductB : public MyProduct
{
public:
  void Iam() { cout << "ProductB" << endl; }
};

int _tmain(int argc, _TCHAR* argv[])
{
  MyProduct *prd;
  MyProductFactory::Register<ProductA>("A");
  MyProductFactory::Register<ProductB>("B");

  prd = MyProductFactory::Create("A");  
  prd->Iam();
  delete prd; 
  prd = MyProductFactory::Create("B");  
  prd->Iam();
  delete prd;

  return 0;
}

It will not compile as is, complaining that the map does not have a valid template type argument for the data type. But if you remove the comments around the 'typename' keyword in the static member definition, everything compiles and works fine... why?

and also, can I make this any better? :)


The standard tries to allow an implementation to parse and detect as many errors in a template as possible when it reads the template definition, before any instantiations. C++ is not context independent, however, and it's very difficult, if not impossible, to correctly parse statements if you don't know which symbols are types and which are templates. If the symbol is dependent (depends on the template parameters in some way), you have to tell the compiler when it is a type or a template; otherwise, the compiler must assume that it is something else. In this case, you're telling the compiler that AbstractFactory::MyFactoryConstructor names a type, and not something else.

If, when the template is instantiated, and the compiler can see to what the symbol is really bound, it turns out that you lied (e.g. AbstractFactory::MyFactoryConstructor is in fact an int), then the compiler will get mad at you.

Note too that the fact that the AbstractFactory was defined before the definition requiring the typedef doesn't change anything. There could always be an explicit specialization for the type you're instantiating AbstractFactory on.


The simple reason is that even though you and I looking at the code know that AbstractFactory::MyFactoryConstructor is a type, the compiler doesn't -- or rather, is prohibited by the standard from knowing this. As far as it knows in the first pass of compilation, MyFactoryConstructor -- itself inside a template yet to be fully realized -- could be something else, like a static variable, which isn't allowed as the second template argument to the map, which requires a type. Supplying "typename" permits the compiler to treat it as a type as soon as it is first encountered.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜