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

时间:2015-01-16 16:05:32

标签: c++ templates

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

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)

4 个答案:

答案 0 :(得分:2)

假设您确实需要拥有模板并且必须具有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的重载可用于派生类型。

答案 1 :(得分:2)

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

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

为避免这种情况,请定义上面的模板,您的代码将会编译,但可能它仍然无法完成您想要的操作。

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

只有在推导出的模板参数类型不是Base或从{{1}派生的类型时,才能使用enable_if来允许第一个模板参与重载解析,从而实现所需的行为}。

Base

然后更改另一个template <typename T> typename std::enable_if<!std::is_base_of<Base, T>::value>::type f(const T& value) { } ,使其不是专业化,而只是过载。

f

Live demo

通常,更喜欢重载功能模板而不是专业化。阅读this以了解功能模板专业化的缺陷。

答案 2 :(得分:1)

问题在于:

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)更适合。

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

答案 3 :(得分:1)

与其他人所说的一样,你还没有定义通用模板函数,它是派生类型的最佳候选者。如果您想更好地理解模板函数重载解析的工作原理,请参考此参考:http://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading

你的例子有点做作(也许是为了简洁起见)。但一般来说,如果需要为特定类型实现特殊逻辑,或者如果要在单独的源文件中定义模板类和函数,并且只需要/想要,则应该只提供模板特化支持某些类型。也许这就是你真正想要的东西,但是仿制药被称为仿制药。