将派生的 class 传递给专门使用基 class 的模板函数

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

以下代码可以正常编译,但会产生 linker 错误:

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;
}

是否可以在不为派生的 class 添加重复的 f() 特化的情况下编译和 link 代码?

谢谢。

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

P.P.S link错误是:

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);

此函数未定义,但它是您调用时编译器的最佳匹配

f(d)

Derived d;

Base 特化 template<> void f(const Base& value) 参与了 f(d) 的重载决议,但是它不如完整模板 template <typename T> void f(const T& value).

合适

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

假设您确实需要模板并且必须具有 Base 的所有派生来调用特化,您可以用重载替换特化并禁用从 [=11 派生的所有类型的模板=] 使用类型特征:

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);

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

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

您可以通过使用 enable_if 实现您想要的行为,仅当推导的模板参数类型不是 Base 或派生自 [= 的类型时,才允许第一个模板参与重载决议16=].

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) {
    // ...
}

Live demo

一般来说,重载函数模板胜过特化。阅读 this 以了解函数模板特化的陷阱。

正如其他人所说,您还没有定义泛型模板函数,它是派生类型的最佳候选者。如果您想更好地了解模板函数重载决策的工作原理,请在此处查阅此参考资料:http://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading

你的例子有点做作(可能是为了简洁起见)。但一般来说,如果您需要为特定类型实现特殊逻辑,或者如果您想要在单独的源文件中定义模板 class(es) 和函数,并且仅need/want 支持某些类型。也许这就是您真正想要的,但是泛型被称为泛型是有原因的。