【问题标题】:Template Specialization Not Working with Derived Class模板专业化不适用于派生类
【发布时间】:2014-09-23 17:04:59
【问题描述】:

我正在为我的一门课程做模板专业化,我遇到了一些意想不到的事情..

这是我的代码:

class Base {};
class Derived : public Base {};

template<typename T>
void doSomething(T t) { cout << "All Types!" << endl; }

template<>
void doSomething(Base b) { cout << "Base!" << endl; }

int main() {
    Derived d;
    doSomething(d);       //prints "All Types!"

    return 0;
}

我有一个模板函数 doSomething(T),它接受任何类型的参数.. 除了类型基类。

所以我将 doSomething 模板专门用于 Base 类型的参数,所以它做了一些不同的事情。

但是,当我将 Derived 类传递给 doSomething 时,它会打印“All Types!”,而我希望它会打印“Base!”,因为 Derived 类本质上也是 Base 类。

为什么这个专业化不适用于 Derived?

有什么办法让它工作?

谢谢

>>> Test Link <<<


更新:

有人提到覆盖而不是模板专业化..但是在这种情况下我将如何覆盖函数?

如果我有:

template<typename T>
void doSomething(T t) { cout << "All Types!" << endl; }

void doSomething(Base b) { cout << "Base!" << endl; }    

然后 doSomething(d) 也会打印“所有类型!”而不是“Base!”,因为 Derived2 对象将被简单地视为类型模板参数

【问题讨论】:

  • 第 1 步:停止专门化功能。改为覆盖。压倒性和专业化以令人讨厌的方式交互。然后考虑覆盖。
  • @Yakk 请看我的更新
  • 你真的打算对你的 Derived 对象进行切片吗?
  • @n.m 我认为这对我来说并不重要。我只希望函数重载/专业化工作..
  • 它是否真的适用于 Base 对象本身?

标签: c++ templates


【解决方案1】:

当您doSomething(Derived) 时,您会导致您的templateT=Derived 推测性地实例化。

这行得通(没有 SFINAE),所以它成为候选者。

doSomething(Base) 要么不被考虑,要么比doSomething(Derived) 更差。

专业化只是更改doSomething 的一个实例化的实现。它根本不会改变它的考虑方式。

覆盖添加另一个覆盖,然后使用常规规则与您的template 版本竞争。

有几种方法可以将调用路由到doSomething,这些调用通过Base 或从基类派生的任何类传递到单个实现。我会显示 2。

首先,标签调度。

namespace aux {
  template<class T> void doSomething( std::true_type /* is Base */, T t ) {
    // T is a class derived from Base
  }
  template<class T> void doSomething( std::false_type /* is Base */, T t ) {
    // T is not class derived from Base
  }
}
template<class T> void doSomething( T t ) {
  aux::doSomething( std::is_base_of< Base, T >{}, std::forward<T>(t) );
}

(将{}替换为(),并在C++03中删除std::forward&lt;T&gt;

在这里,我们将Base 的派生类显式路由到不同的覆盖。

另一种方法是 SFINAE 将 template 排除在考虑之外,并进行覆盖:

template<class T>
typename std::enable_if< !std::is_base_of<Base, T>::value >::type
doSomething( T t ) { /* blah */ }

void doSomething( Base b ) { /* foo */ }

现在,template 版本已被 SFINAE 排除在外,而是使用覆盖。

我发现标签调度更干净。

【讨论】:

  • 哇,很棒的解决方案。顺便说一句,您是否有理由在第二个解决方案中的 doSomething(T t) 声明前省略“void”?
  • 嗯,我想我们之前讨论过——override 一词是否适合标准调用的 overload,尤其是考虑到新的“关键字"override?它在这个意义上是常用的吗? (我是不是太迂腐了?)我很困惑
  • @user2436815 enable_if 如果通过了 SFINAE,则提供 void
【解决方案2】:

你可以用 std::enable_if: 解决它

#include <type_traits>
#include <iostream>

class Base {};
class Derived1 : public Base {};
class Derived2 : public Derived1 {};

template<typename T>
typename std::enable_if< ! std::is_base_of<Base, T>::value>::type
doSomething(const T&) { std::cout << "All Types!" << std::endl; }

template<typename T>
typename std::enable_if<std::is_base_of<Base, T>::value>::type
doSomething(const T&) { std::cout << "Base or Derived!" << std::endl; }

int main() {
    // All Types!
    doSomething(1);
    // Base or Derived!
    doSomething(Base());
    doSomething(Derived1());
    doSomething(Derived2());

    return 0;
}

注意:参数是const T&amp;,但使用 T 作为参数可以避免上述切片。

注意:typename std::enable_if&lt;std::is_base_of&lt;Base, T&gt;::value&gt;::type是返回类型,是std::enable_if的可选第二个模板参数,默认为void。

【讨论】:

  • 请注意,这会创建两个不同的 doSomething 覆盖,它不会专门化。
  • 很棒的解决方案!!顺便说一句,您是否有理由在 doSomething(const T&) 声明前省略“void”?
  • 哦,所以如果返回类型是 bool 而不是 void,我可以这样做:“typename std::enable_if::value, bool>: :type" ?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-04
  • 2014-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多