【问题标题】:typename when defining map data that is a function pointer with a sprinkling of templates定义地图数据时的类型名,该数据是带有模板的函数指针
【发布时间】:2024-01-13 19:15:01
【问题描述】:

这是一个奇怪的问题,因为我已经知道“编码”的答案。我只是想更好地理解为什么会这样。这里有一些大师比 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'关键字周围的cmets,一切都会编译并且工作正常......为什么?

还有,我能把它做得更好吗? :)

【问题讨论】:

标签: c++ templates map factory typename


【解决方案1】:

该标准试图允许实现解析和 读取时尽可能多地检测模板中的错误 模板定义,在任何实例化之前。 C++ 不是 然而,与上下文无关,如果不是的话,这非常困难 不可能,如果你不知道正确解析语句 哪些符号是类型,哪些是模板。如果符号 是依赖的(以某种方式依赖于模板参数), 当它是类型或模板时,您必须告诉编译器; 否则,编译器必须假定它是别的东西。 在这种情况下,您是在告诉编译器 AbstractFactory::MyFactoryConstructor 命名一个类型, 而不是别的东西。

如果,当模板被实例化时,编译器可以看到 对符号真正绑定的东西,原来你撒了谎 (例如 AbstractFactory::MyFactoryConstructor 实际上是 int),那么编译器会生你的气。

还要注意,AbstractFactory 已被定义 在需要 typedef 的定义没有改变之前 任何事物。总是可以有一个明确的专业化 您正在实例化 AbstractFactory 的类型。

【讨论】:

    【解决方案2】:

    原因很简单,即使您和我在查看代码时都知道 AbstractFactory::MyFactoryConstructor 是一种类型,但编译器并不知道——或者更确切地说,标准禁止编译器知道这一点。据它在编译的第一遍中所知,MyFactoryConstructor——它本身在一个尚未完全实现的模板中——可能是其他东西,比如一个静态变量,它不允许作为映射的第二个模板参数,这需要一个类型。提供“typename”允许编译器在第一次遇到它时将其视为类型。

    【讨论】: