将派生类传递给专用于基类的模板函数

Passing a derived class to a template function specialized with base class

本文关键字:基类 函数 用于 专用 派生      更新时间:2023-10-16

以下代码编译正常,但会产生链接器错误:

class Base {};
class Derived : public Base {};
template <typename T>
void f(const T& value);
template <>
void f(const Base& value) {
    // ...
}
int main() {
    Base b;
    f(b);
    Derived d;
    f(d); // This line causes linker error.
    return 0;
}

是否可以在不为派生类添加重复f()专用化的情况下使此代码编译和链接?

谢谢。

P.S 我正在使用 clang,Apple LLVM 版本 6.0 (clang-600.0.56)(基于 LLVM 3.5svn)。

P.P.S 链接器错误为:

Undefined symbols for architecture x86_64:
  "void f<Derived>(Derived const&)", referenced from:
      _main in test2-4245dc.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

发生链接器错误是因为尚未定义(仅声明)函数模板

template <typename T>
void f(const T& value);
为了避免它,

定义上面的模板,你的代码将编译,但大概它仍然没有做你想要的。

当使用类型为 Derived 的参数调用 f 时,与您的专用化相比,上述模板更匹配,因为后者需要派生到基础的转换,而前者不需要。

通过使用 enable_if 仅当推导的模板参数类型不是 Base 或派生自 Base 的类型时,才允许第一个模板参与重载解析,从而实现所需的行为。

template <typename T>
typename std::enable_if<!std::is_base_of<Base, T>::value>::type
    f(const T& value) {
}

并改变其他f,使其不是专业化,而只是超载。

void f(const Base& value) {
    // ...
}

现场演示

通常,首选重载函数模板而不是专用化。阅读本文以了解函数模板专用化的陷阱。

假设您确实需要模板,并且必须具有Base的所有派生物才能调用专用化,则可以将专用化替换为重载,并为使用类型特征从Base派生的所有类型禁用模板:

template <typename T, typename std::enable_if<!std::is_base_of<Base, T>::value>::type>
void f(const T& value);
// no template
void f(const Base& value)

这样,只有 Base 的重载可用于派生类型。

问题就在这里:

template <typename T>
void f(const T& value);

此函数未定义,但是当您调用

f(d)

Derived d;

Base专用化template<> void f(const Base& value)参与f(d)的重载解析,但它不如完整模板template <typename T> void f(const T& value)合适。

一个想法是完全删除专用化,只定义完整的模板,代码可能会做你想要的。

正如其他人所说,您尚未定义泛型模板函数,它是派生类型的最佳候选项。如果要更好地了解模板函数重载解析的工作原理,请参阅此处的参考:http://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading

你的例子有点做作(也许是为了简洁起见)。但通常,仅当需要为特定类型实现特殊逻辑,或者希望在单独的源文件中定义模板类和函数并且只需要/想要支持某些类型时,才应提供模板专用化。也许这就是您实际想要的,但泛型被称为泛型是有原因的。