【问题标题】:Calling a super template class method without knowing the template type in C++在 C++ 中不知道模板类型的情况下调用超模板类方法
【发布时间】:2018-09-26 11:34:07
【问题描述】:

我有一个使用模板的基类,它有一些不依赖于模板类型的方法,但是当我使用指针 Base* a 而不是派生类时,编译器会抱怨,因为没有指定类型.我知道在 java 中这是可能的,但不确定在 C++ 中是否可能。这里举个简单的例子:

template <typename T>
class Base {
public:
    Base(const T& t) : _t(t) {}
    virtual ~Base() { }

    void doSomething() { std::cout << "Hello world/n"; }

    virtual T getVal() const { return _t; }

private:
    T _t;
};

class DerivedA : public virtual Base<std::string>
{
public:
    DerivedA(const std::string& path) : Base<std::string>(path) {}
    virtual ~DerivedA() {}
};

class DerivedB : public virtual Base<int>
{
public:
    DerivedB(int value) : Base<int>(value) {}
    virtual ~DerivedB() {}
};

int main(int argc, const char * argv[]) {

    DerivedA d("hello world\n");
    Base* basePtr = &d; // ERROR: Use of class template 'Base' requires template arguments
    basePtr->doSomething();

提前致谢

【问题讨论】:

  • 如果你的函数不依赖于签名中的任何模板参数,比如doSomething函数,那么你可以创建一个非模板“真实”基类,模板化@987654324 @类继承自。
  • 你不能这样做。 Base&lt;std::string&gt;Base&lt;int&gt; 是不同的专业,因此它们是不同的类型。你不能把苹果扔进车里……
  • 不要将 java 泛型与 c++ 模板混淆,这是两个截然不同的概念
  • 使用具体(非模板)基类的建议通常是正确的,但值得检查 - 为什么你想要一个基类指针?你用它做什么?

标签: c++ templates


【解决方案1】:

只需创建另一个不是模板的基类:

class ReallyBase {
public:
    virtual ~ReallyBase() = default;
    void doSomething() { std::cout << "Hello world\n"; }
};

template <typename T>
class Base : public ReallyBase {
public:
    Base(const T& t) : _t(t) {}
    virtual const T& getVal() const { return _t; }    
private:
    T _t;
};

【讨论】:

  • 我怀疑ReallyBase::doSomething 仍应在此处公开,ReallyBase 可能也值得一个虚拟 dtor。
  • @Useless:谢谢你不辜负你的名字。
【解决方案2】:

Java 中的泛型与 C++ 中的模板完全不同。泛型基于类型擦除,而对于模板,每个实例化都是一个单独的类型,与不同的实例化完全无关。

即在Java 中ArrayList&lt;Integer&gt;ArrayList&lt;Boolean&gt; 基本相同。另一方面,对于模板,Base&lt;int&gt;Base&lt;std::string&gt; 没有任何关系(除了由同一模板产生的两种类型),因此您的派生类实际上共享一个公共基类。

要解决您的问题,您可以编写一个(非模板)基类来声明所有派生类的公共接口。

【讨论】:

    【解决方案3】:

    让我们1st谈谈你的问题的简单解决方案,写一个推导函数:

    template<typename T>
    Base<T>* make_base(Base<T>* param) {
        return param;
    }
    

    您可以按如下方式使用它:

    DerivedA d("hello world\n");
    auto basePtr = make_base(&d);
    
    basePtr->doSomething();
    

    这个解决方案可以像this example 一样进一步改进,但由于我认为这不是最好的解决方案,我不会用它来混淆答案,而是将这种优化留给你细读。

    但是2nd让我们谈谈改进您的设计。您会注意到任何标准容器都会创建一个using value_type =...,从而存储它们传递的模板类型。这对于这种确切的情况很重要!如果您将using value_type = T 公开添加到您的Base 模板类中,您将可以规避您遇到的所有问题,而无需make_base,您将能够做到:

    Base<decltype(d)::value_type>* basePtr = &d;
    

    这里一个重要的后续概念是DerivedADerivedB 确实具有相同的基类。因此你永远不能做这样的事情

    auto basePtr = make_base(&d);
    basePtr = new DerivedB({});
    

    【讨论】:

      猜你喜欢
      • 2012-01-02
      • 1970-01-01
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      • 2011-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多