具有非指针/引用返回类型的协变返回类型

Fra*_*ffa 2 c++ ienumerable pointers return-type covariant

我正在尝试在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);
    }
};
Run Code Online (Sandbox Code Playgroud)

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

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

Enumerator *GetEnumerator()
{
    return new Enumerator(*this);
}
Run Code Online (Sandbox Code Playgroud)

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

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

提前致谢.

Dav*_*eas 5

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

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

在与@curiousguy讨论相当荒谬(在我这边)之后,我必须从之前的回答中回溯.没有技术问题会导致协变值返回类型无法实现.另一方面,它会产生不同的负面影响:

从设计角度来看,如果从base调用返回的对象,则必须对其进行切片(这是返回对象的大小很重要的地方).这与当前模型明显不同,在当前模型中函数始终返回相同的对象,它只是更改类型的引用或指针.但实际的对象是一样的.

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

无论如何,操作的实际成本不会像调用最终覆盖的语义不同*这一事实那么多,这取决于执行调用的引用的静态类型是什么.


*当前语言定义已经是这种情况.对于所有非虚函数,如果派生类型在基础上隐藏成员函数,则返回指针/引用的静态类型(这又取决于用于调用虚函数的静态类型)将影响函数获取的函数实际调用,行为不同.