4

This code:

template <typename T>
struct A
{
     T t;

     void DoSomething()
     {
          t.SomeFunction();
     }
};

struct B
{
};

A<B> a;

is easily compiled without any complaints, as long as I never call a.DoSomething().

However, if I define DoSomething as a virtual function, I will get a compile error saying that B doesn't declare SomeFunction. I can somewhat see why it happens (DoSomething should now have an entry in the vtable), but I can't help feeling that it's not really obligated. Plus it sucks.

Is there any way to overcome this?

EDIT 2: Okay. I hope this time it makes sence: Let's say I am doing intrusive ref count, so all entities must inherit from base class Object. How can I suuport primitive types too? I can define:

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

so I can use Primitive<int>, Primitive<char>... But how about Primitive<float>? It seems like a problem, because floats don't have a %= operator. But actually, it isn't, since I'll never call operator %= on Primitive<float>. That's one of the deliberate features of templates.

If, for some reason, I would define operator %= as virtual. Or, if i'll pre-export Primitive<float> from a dll to avoid link errors, the compiler will complain even if I never call operator %= on a Primitive<float>. If it would just have fill in a dummy value for operator %= in Primitive<float>'s vtable (that raises an exception?), everything would have been fine.

4

4 回答 4

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 与作为 Methods 模板参数提供的 Jumper、Crawler 或 JumperCrawler 一起使用;意识到您需要从它派生,以便您可以在子类中实现跳跃和/或爬行。

仅供参考,这使得“ICanBoostJumpingAndCrawling”这个名称完全具有误导性,因为它可能会也可能不会这样做;这意味着它应该重命名为“Booster”之类的东西。

于 2009-09-28T23:52:15.967 回答
0

所以编译器应该能够在编译单元中计算出正在使用的内容。一旦您开始涉及多个编译单元,它就不再具有有限的范围,并采取必要的步骤来确保所有类都可以编译。

对于从库中导出不要强制预导出,您可以忽略有关不导出模板的警告,只要您使用相同的编译器编译所有代码,模板将在所有位置编译相同,只编译什么在每个编译单元中都是必需的。

要解决 virtual 的问题,最好的办法是将问题推迟到其他类 - 不要将 virtual 放在模板中。

也许

  • 在您的模板中添加“特征”部分将提供一种廉价的出路。
  • 使用多重继承来定义复合模板,例如使用 shims

IE。

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

我真的会回去询问您是否为制作模板提取了正确级别的信息。

于 2009-09-29T02:12:39.763 回答
0

这不是一个错误,它是一个特性——说真的。有一次,大多数编译器不会编译代码,这正是您给出的原因。他们已经被更新来编译它,部分原因是标准需要它。

在 C++ 0x 标准中有一段时间称为“概念”的特性允许您指定 T 需要一个名为“SomeFunction”的成员,包括它的返回类型、参数类型等。

可悲的是,在标准委员会的最后一次会议上,他们决定完成概念会使大多数人想要等待的标准延迟更长的时间,所以他们删除了它们。

虽然它没有那么好,但 Boost 确实有一个概念检查库,可以满足您的要求。

于 2009-09-28T23:20:45.243 回答
0

克服这个问题的一种方法是将 A 专门用于模板参数 B 而不是声明 DoSomething()

template <>
struct A<struct B>
{
     T t;

};

当然,这意味着您现在必须从头开始实现整个 A 结构。

于 2009-09-28T23:22:37.707 回答