2

我正在尝试在 C++(11) 中实现一个.NET 框架,如集合类。我的问题是无效的协变类型。我有这些课程:

template<typename T>
class IEnumerator
{
public:
    virtual bool MoveNext() = 0;
    //...
};

template<typename T>
class IEnumerable
{
    virtual IEnumerator<T> GetEnumerator() = 0;
};

template<typename T>
class List : public IEnumerable<T>
{
public:
    struct Enumerator : public IEnumerator<T>
    {
        Enumerator(List<T> &list)
        {
            //...
        }
        // ...
    };

    Enumerator GetEnumerator()
    {
        return Enumerator(*this);
    }
};

在我看来,这太棒了。但是在 C++ 中实现它看起来是不可能的。我得到 g++ 的“无效协变返回类型”,据我所知,问题是 GetEnumerator 可能只返回一个指针或对 Enumerator 的引用,而不是 Enumerator 本身的对象。

我想避免返回这样的指针:

Enumerator *GetEnumerator()
{
    return new Enumerator(*this);
}

因为我不希望调用者打扰删除。使用临时对象,我可以确保该对象被自动删除,因为它不再需要了。使用引用可能会更糟。

我错过了什么吗?还是 C++ 标准(和语言)存在巨大漏洞?我真的很想实现这样的目标。

提前致谢。

4

3 回答 3

5

无法实现协变值返回类型。问题是调用者有责任在堆栈中为返回的对象分配空间,而协变值返回所需的空间量在编译时是未知的。

这与指针/引用无缝工作,因为返回的对象是指针或引用(而不是实际的派生对象),并且大小在编译时是已知的。

在与@curiousguy 进行了相当荒谬的(在我这边)讨论之后,我必须从之前的答案中回溯。没有技术问题会使协变值返回类型成为不可能。另一方面,它会产生不同的负面影响:

从设计的角度来看,如果从基础调用返回的对象必须被切片(这是返回对象的大小很重要的地方)。这与当前模型有明显的区别,在当前模型中,函数总是返回相同的对象,改变类型的只是引用或指针。但实际对象是一样的。

在一般情况下,协变值类型会抑制一些复制省略优化。目前,对于按值返回的函数,许多调用约定规定调用者将指针传递给返回对象的位置。这允许调用者保留将保存该值的变量空间,然后传递该指针。然后,被调用者可以使用该指针来代替将在调用者上下文中保存值的对象进行构造,并且不需要副本。使用协变值返回类型,并且由于必须销毁最终覆盖器创建的最派生对象以避免未定义的行为。调用者将传递一个指向内存中某个位置的指针,蹦床函数必须为最终覆盖器的返回对象保留空间,然后它需要从第二个对象到第一个对象的切片复制,会产生复制成本。

无论如何,操作的实际成本不会像调用最终覆盖程序的语义会不同*取决于执行调用的引用的静态类型那样成为问题.


*这已经是当前语言定义的情况。对于所有非虚函数,如果派生类型在基类上隐藏了一个成员函数,那么返回的指针/引用的静态类型(这又取决于用于调用虚函数的静态类型)将影响函数得到什么实际调用并且行为不同。

于 2012-05-25T18:03:31.197 回答
1
template<typename T>
class IEnumerable
{
    virtual IEnumerator<T> GetEnumerator() = 0;
};

您试图返回 aIEnumerable<T>但它是一个抽象基类:这意味着您承诺构造一个无法实例化的类的对象!

只有从抽象基类派生的具体类才能被实例化

您可能打算返回指向此类对象的指针。无论如何,这是一个糟糕的设计。您不需要在 C++ 中模拟 Java。

于 2012-08-14T21:40:31.633 回答
0

除非您使用指针,否则您不会在 C++ 中做类似的事情。.net 家伙使用引用,所以它几乎是相同的东西。

在 C++ 中,您更有可能使用概念而不是继承来实现这一点。您应该审查“通用编程”的想法。boost网站有一个不错的介绍: http: //www.boost.org/community/generic_programming.html

于 2012-05-25T18:32:11.247 回答