我有一个带有模板成员的基类,它专门用于某些情况。派生类进一步专门化了基类的模板成员。除此之外的基本原理是,模板成员的各种专业化都“适应”特定情况,“逻辑上”执行相同的工作。基类提供了一些模板特化,这些特化可在某些情况下执行任务,派生类应通过进一步特化模板成员来将同一任务“扩展”到其他情况。
这是说明我遇到的问题的最小示例。
#include <iostream>
struct A {
template <int i>
void dosomething();
void show();
};
template<>
void A::dosomething<0>()
{
std::cout << "0 in A" << std::endl;
}
void A::show()
{
dosomething<0>();
}
struct B : A {
// Without the following declaration:
// error: template-id ‘dosomething<1>’ for ‘void B::dosomething()’
// does not match any template declaration
template <int i>
void dosomething();
};
template<>
void B::dosomething<1>()
{
std::cout << "1 in B" << std::endl;
}
int main()
{
A x;
x.dosomething<0>();
B y;
y.dosomething<0>(); // Link error!
y.show();
y.dosomething<1>();
return 0;
}
模板成员A::dosomething()
在基类中显式专用于i=0
。模板的代码是显式生成的,并在成员A::show()
中调用。
我发现的第一个问题是:
A)没有重复的声明
template <int i>
void dosomething();
在B
的定义内,代码无法编译,并显示错误:
template-id ‘dosomething<1>’ for ‘void B::dosomething()’
does not match any template declaration.
为什么基类A
中的先前声明不可见?
B)上面的代码引起了链接错误:
undefined reference to `void B::dosomething<0>()'
该错误是由于main中的调用y.dosomething<0>()
引起的。可以通过调用y.A::dosomething<0>()
来避免。为什么dosomething<0>()
在B
的实例中显然不可见?
答案 0 :(得分:3)
当您对成员函数进行out-of-line
定义时,将在::
运算符之前引用的类中查找该函数的声明。
考虑一下:
struct C { void test(); };
struct D : C { };
void D::test() { }; // error, test is not a member of D but of C
这与
template<> void B::dosomething<1>()
{ }
dosomething
及其所有专业化定义必须由声明其的类(即在A
中声明的类)进行限定,就像对dosomething<0>
所做的方式一样。
还要注意,dosomething
中B
的声明与A
的声明完全无关。您由于调用未定义的专业化B::dosomething<0>
而收到链接错误。
您可以创建特殊化template<> void A::dosomething<1>(){ }
,但是您并没有得到预期的多态行为,如果您确实需要A::dosomething<1>
的不同版本,则所有派生类都将共享dosomething<1>
{1}}受子类限制,您仅限于初始重复,并且为了从A::dosomething<0>
访问B
,您必须像static_cast<A&>(b).dosomething<0>()
那样进行操作。
您还应该查看此answer
中的静态多态性