typename при определении данных карты, представляющих собой указатель на функцию с разбросом шаблонов - PullRequest
1 голос
/ 17 марта 2011

Это странный вопрос, потому что я уже знаю ответ «кодирование». Я просто хочу лучше понять, почему это так. Здесь есть гуру, которые умеют объяснять эти вещи лучше, чем стандарт C ++:)

Ниже у нас есть средство для определения абстрактного фабричного шаблона, который выделяет объекты на основе строки в качестве ключа (это надуманный пример): -

#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>
map<string, /*typename*/ AbstractFactory<TProduct>::MyFactoryConstructor> AbstractFactory<TProduct>::mTypes;

Вот пример того, как мы его используем: -

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;
}

Он не будет компилироваться как есть, жалуясь, что на карте нет допустимого аргумента типа шаблона для типа данных. Но если вы удалите комментарии вокруг ключевого слова typename в определении статического члена, все компилируется и работает нормально ... почему?

а также, могу ли я сделать это лучше? :)

Ответы [ 2 ]

2 голосов
/ 17 марта 2011

Стандарт пытается разрешить реализации анализировать и обнаруживать как можно больше ошибок в шаблоне, когда он читает определение шаблона, перед любыми экземплярами.Однако C ++ не зависит от контекста, и очень трудно, если не невозможно, правильно проанализировать операторы, если вы не знаете, какие символы являются типами, а какие - шаблонами.Если символ является зависимым (зависит от параметров шаблона в некотором роде), вы должны сообщить компилятору, когда это тип или шаблон;в противном случае компилятор должен предположить, что это нечто другое.В этом случае вы сообщаете компилятору, что AbstractFactory :: MyFactoryConstructor называет тип, а не что-то еще.

Если, когда создается экземпляр шаблона, и компилятор может видеть, с чем действительно связан символ, оказывается, что вы солгали (например, AbstractFactory :: MyFactoryConstructor на самом деле int), тогда компилятор рассердится на вас.

Обратите также внимание, что тот факт, что AbstractFactory был определен до определения, требующегоtypedef ничего не меняет.Всегда может быть явная специализация для типа, для которого вы создаете экземпляр AbstractFactory.

2 голосов
/ 17 марта 2011

Простая причина в том, что, хотя вы и я, глядя на код, знаем, что AbstractFactory :: MyFactoryConstructor является типом, компилятор не запрещает - или, скорее, стандарт не знает этого. Насколько известно в первом проходе компиляции, MyFactoryConstructor - сам внутри шаблона, который еще не полностью реализован - может быть чем-то другим, например, статической переменной, которая не допускается в качестве второго аргумента шаблона для карты который требует типа. Указание «typename» позволяет компилятору обрабатывать его как тип, как только он встречается впервые.

...