此代码:
template <typename T>
struct A
{
T t;
void DoSomething()
{
t.SomeFunction();
}
};
struct B
{
};
A<B> a;
只要我从不打电话给a.DoSomething()
,很容易编译而没有任何投诉。
但是,如果我将DoSomething
定义为虚函数,我将收到编译错误,指出B
未声明SomeFunction
。我可以在某种程度上看到它为什么会发生(DoSomething现在应该在vtable中有一个条目),但我不禁觉得它并没有真正的义务。加上它很糟糕。
有没有办法克服这个问题?
编辑2:好的。我希望这次它能够做到: 假设我正在进行侵入式引用计数,因此所有实体都必须从基类Object继承。我怎样才能成为原始类型?我可以定义:
template <typename T>
class Primitive : public Object
{
T value;
public:
Primitive(const T &value=T());
operator T() const;
Primitive<T> &operator =(const T &value);
Primitive<T> &operator +=(const T &value);
Primitive<T> &operator %=(const T &value);
// And so on...
};
所以我可以使用Primitive<int>
,Primitive<char>
...
但Primitive<float>
怎么样?这似乎是一个问题,因为浮点数没有%=
运算符。但事实上,事实并非如此,因为我永远不会在operator %=
上致电Primitive<float>
。
这是模板的深思熟虑的功能之一。
如果出于某种原因,我会将operator %=
定义为虚拟。或者,如果我要从dll预导出Primitive<float>
以避免链接错误,即使我从未在operator %=
上调用Primitive<float>
,编译器也会抱怨。如果它只是在operator %=
的vtable中填充Primitive<float>
的虚拟值(引发异常?),那么一切都会好的。
答案 0 :(得分:2)
将虚拟内容放入可选择的基类......
struct Jumper
{
virtual void Jump =0;
};
struct Crawler
{
virtual void Crawl() =0;
};
struct JumperCrawler:
public Jumper,
public Crawler
{
};
template<typename T, typename Methods>
class ICanBoostJumpingAndCrawling :
public Methods
{
T t;
};
现在,您可以将ICanBoostJumpingAndCrawling与Jumper,Crawler或JumperCrawler一起用作Methods模板参数;意识到你需要从它派生,以便你可以在子类中实现跳跃和/或爬行。
仅供参考,这使得名称“ICanBoostJumpingAndCrawling”完全具有误导性,因为它可能会也可能不会这样做;这意味着它应该被重命名为“Booster”。
答案 1 :(得分:0)
这不是一个错误,它是一个功能 - 认真。有一次,大多数编译器都不会编译代码,正是因为你提供的原因。它们已被更新以编译它,部分原因是标准需要它。
很长一段时间,在C ++ 0x标准中有一个名为“Concepts”的功能,它允许你指定T需要一个名为'SomeFunction'的成员,包括它的返回类型,参数类型等。
可悲的是,在标准委员会的最后一次会议上,他们决定完成概念会延迟大多数人想要等待的标准,所以他们删除了它们。
虽然它不是那么好,但Boost确实有一个Concept Checking库可以做你想要的。
答案 2 :(得分:0)
解决此问题的一种方法是将A专用于模板参数B而不是声明DoSomething()
template <>
struct A<struct B>
{
T t;
};
当然,这意味着您现在必须从头开始实现整个A结构。
答案 3 :(得分:0)
因此编译器应该能够在编译单元中解决正在使用的问题。一旦你开始涉及多个编译单元,它就不再具有有限的范围,并采取必要的步骤来确保所有类都可以编译。
对于从库导出不强制预导出,只要使用相同的编译器编译所有代码,就可以忽略有关不导出模板的警告,模板将在所有位置编译相同的模板,仅编译每个编译单元中的必要内容。
要解决虚拟问题,那么您可以做的最好的事情是将问题推迟到其他类 - 不要将虚拟放在模板中。
也许
即
template <typename T>
class Additive
{
public:
Primitive<T> &operator =(const T &value);
Primitive<T> &operator +=(const T &value);
};
template <typename T>
class Multiplicative
{
public:
Primitive<T> &operator *=(const T &value);
Primitive<T> &operator /=(const T &value);
};
template <typename T>
class Integers : public Additive<T>, public Multiplicative<T>;
我真的回过头来询问你是否抽出了制作模板的正确信息。