首页 > 解决方案 > 带有子类的模板实例化和特化顺序

问题描述

我试图围绕模板的实例化和专业化的顺序,特别是在涉及子类的情况下。

请考虑以下示例:

class A {
  int value() { return 42; }
};
class B {
  double value() { return 3.1415; }
};

class MyBase {
public:
  virtual int value() = 0;
};

template<class T> class MyClass : public MyBase {
protected:
  class Cache;
  mutable Cache _cache;
public:
  MyClass(T t) : _cache(t.value()) {};
  virtual int value() override;
};

template<class T> class MyClass<T>::Cache {
public:
  int x;
  int getVal(){return x;}
};

template<> class MyClass<B>::Cache {
public:
  double z;
  int getVal(){return 100*z;}  
};

template<class T> int MyClass<T>::value() { return _cache.getVal(); }

typedef MyClass<A> MyA;
typedef MyClass<B> MyB;    

这导致error: specialization of ‘MyClass<B>::Cache’ after instantiation,我不明白。我猜想MyClass<B>::Cache只有在发生这种情况时才需typedef要这样做,但似乎并非如此。MyClass我试图在通用模板定义之前,在定义内部甚至在此之前(使用前向声明)稍微移动一下专业化,但没有任何运气。

有没有合适的方法来实现这样的事情,或者这在逻辑上是不可能的,因为我很高兴能更好地理解这些原因?

我想我可以通过创建一个指针来实现这一点,_cache这样在定义时不需要完整的定义MyClass,但我宁愿避免这样做,除非我了解潜在的问题。

标签: c++templates

解决方案


给读者的其他提醒:

  1. 我们不能在班级范围内进行专业化:demo
  2. 我们不能转发声明内部类
  3. 我们可以完全专门化内部类(参见cppref 6。)

第一个解决方案:

我们专门化整个类,所以我们可以有一个专门化的内部类。现场演示

#include <iostream>
#include <stdexcept>

struct A {
  int value() { return 42; }
};
struct B {
  double value() { return 3.1415; }
};

class MyBase {
public:
  virtual int value() = 0;
};

template <class T> class MyClass : public MyBase {
protected:
  struct Cache {
    int x;
    int getVal() { return x; }
    Cache(double v) : x(v){}; // gcc11 need this in c++17, not in c++20
  };

public:
  mutable Cache _cache;

public:
  MyClass(T t) : _cache(t.value()){};
  virtual int value() override;
};

template <> class MyClass<B> : public MyBase {
protected:
  struct Cache {

    double z;
    Cache(double v) : z(v){}; // gcc11 need this in c++17, not in c++20
    int getVal() { return 100 * z; }
  };

  mutable Cache _cache;

public:
  MyClass(B t) : _cache(t.value()){};
  virtual int value() override;
};

template <class T> int MyClass<T>::value() { return _cache.getVal(); }
int MyClass<B>::value() { return _cache.getVal(); }

typedef MyClass<A> MyA;
typedef MyClass<B> MyB;

int main() {
  MyA a(A{});
  MyB b(B{});

  std::cout << a.value() << " " << b.value() << std::endl;
}

第二种解决方案:

制作Cache模板和外部,MyClass以便我们可以专业化。恕我直言,它更清洁。现场演示。

#include <iostream>
#include <stdexcept>

struct A {
  int value() { return 42; }
};
struct B {
  double value() { return 3.1415; }
};

template <class T> struct Cache {
  int x;
  int getVal() { return x; }
  Cache(double v) : x(v){}; // gcc11 need this in c++17, not in c++20
};

template <> struct Cache<B> {
  double z;
  Cache(double v) : z(v){}; // gcc11 need this in c++17, not in c++20
  int getVal() { return 100 * z; }
};

class MyBase {
public:
  virtual int value() = 0;
};

template <class T> class MyClass : public MyBase {
protected:
public:
  mutable Cache<T> _cache;

public:
  MyClass(T t) : _cache(t.value()){};
  virtual int value() override;
};

template <class T> int MyClass<T>::value() { return _cache.getVal(); }

typedef MyClass<A> MyA;
typedef MyClass<B> MyB;

int main() {
  MyA a(A{});
  MyB b(B{});

  std::cout << a.value() << " " << b.value() << std::endl;
}

我希望它有所帮助,如果我错过了什么,请告诉我。


推荐阅读