C++ 具有赋值语义的非所有持有者

C++ 具有赋值语义的非所有持有者,c++,reference,assignment-operator,C++,Reference,Assignment Operator,我有一个类,它应该持有对某些数据的引用,而不拥有这些数据(即,保证实际数据不会超出范围)。特别是,该类无法创建副本–数据的大小很容易达到几GB 现在,通常的实现(我假设)是引用数据: struct holder_ref { type const& value; holder_ref(type const& value) : value(value) { } }; (请注意,constness与问题完全无关) 现在,我绝对需要这个类是可赋值的(即有一个工作的操作

我有一个类,它应该持有对某些数据的引用,而不拥有这些数据(即,保证实际数据不会超出范围)。特别是,该类无法创建副本–数据的大小很容易达到几GB

现在,通常的实现(我假设)是引用数据:

struct holder_ref {
    type const& value;

    holder_ref(type const& value) : value(value) { }
};
(请注意,
const
ness与问题完全无关)

现在,我绝对需要这个类是可赋值的(即有一个工作的
操作符=
)。我认为这是一个相当普遍的问题,但我不记得以前是如何解决的

问题是不能分配引用,而且根本没有办法解决这个问题。我提出的唯一解决方案是使用新的placement替换赋值运算符:

// x = other_x; gets replaced with:
x.~T();
new (&x) T(other_x);
现在,它可以正常工作,并且符合标准。但它确实很难看。否–不可接受

所以我在寻找替代品。一个想法是使用指针,但我不确定我的构造函数是否真的能保证工作(而且由于我必须遵守的接口,传递指针是不可能的):

但如果可能的话,我宁愿使用参考资料。仅限–如何实现赋值运算符

struct holder_ref {
    type const& value;

    holder_ref(type const& value = 0) : value(value) { }

    holder_ref& operator =(holder_ref const& other) {
        // Now what?!
        return *this;
    }
};

作为测试用例,考虑以下代码:

int main() {
    int const TEST1 = 23;
    int const TEST2 = 13;
    int const TEST3 = 42;
    std::vector<holder_ptr> hptr(1);
    std::vector<holder_ref> href(2);

    // Variant 1. Pointer.
    hptr[0] = holder_ptr(TEST1);

    // Variant 2. Placement new.
    href[0].~holder_ref();
    new (&href[0]) holder_ref(TEST2);

    // Variant 3. ???
    href[1] = holder_ref(TEST3);

    assert(*hptr[0].value == TEST1);   // Works (?)
    assert(href[0].value == TEST2);    // Works
    assert(href[1].value == TEST3);    // BOOM!
}
intmain(){
int const TEST1=23;
int const TEST2=13;
int const TEST3=42;
std::载体hptr(1);
std::vector href(2);
//变量1.指针。
hptr[0]=保持器_ptr(TEST1);
//变式2.新位置。
href[0]。~holder_ref();
新的(&href[0])持有者_ref(TEST2);
//变体3???
href[1]=持有者参考(TEST3);
assert(*hptr[0]。value==TEST1);//工作(?)
断言(href[0].value==TEST2);//有效
断言(href[1].value==TEST3);//轰!
}

(另外,我要说的是,我们谈论的类型是非POD,我需要一个符合标准的解决方案。)

TR1弱ptr标准是否足够符合?

我认为使用
支架ptr没有任何问题。它可以这样实现:

struct bad_holder : std::exception { };

struct holder_ptr {
    holder_ptr() : value(0) { }
    holder_ptr(type const& value) : value(&value) { }

    type const& get() { 
        if (value == 0) throw bad_holder();
        return *value; 
    }
private:
    type const* value;
};
只要您总是从引用中分配给指针,您就知道您有一个有效的对象(或者您以前得到了一个“null引用”,在这种情况下,您还有其他更大的问题,因为您已经调用了未定义的行为)

使用此解决方案,接口完全按照引用实现,但在后台使用了指针,因此类型是可分配的。在接口中使用引用可以确保使用指针时不存在任何问题(即,您永远不必担心指针是否为null)


编辑:我已经更新了示例,以允许保持器是默认可构造的。

我将使用指针保持器。但是,如果你坚决反对,那么隐藏你的新的
操作符=

holder_ref& operator =(holder_ref const& other) {
    new (this) holder_ref(other);
    return *this;
}

指针解决方案有什么问题?它似乎完全符合您的用例。它是可分配成员,不表示或暗示所有权。虽然您不能合法地存储指向临时成员的指针,但您的默认值:
holder\u ptr(type const&value=0)
是不明智的。(但这对引用也适用。)@Charles:是的,这是引用解决方案存在的另一个问题。@Konrad:如果持有者需要是默认可构造的,那么你真的不能存储引用——如果你默认构造引用,你就没有对象可以初始化它。也许我遗漏了什么?@查尔斯:你需要多回答,少评论。总而言之,你的评论对这个问题最有帮助(尽管其他答案也很好)。遗憾的是,我不能奖励它。是的,不幸的是,TR1和Boost(我知道,我知道,愚蠢…)都不适合这个项目。我甚至没有想到这一点。
弱\u ptr
只有在某人拥有具有
共享\u ptr
的数据时才有效。即使是这样,考虑到“保证实际数据不会超出范围”,这也有点毫无意义。哦,我忘记了那个解决方案。漂亮(按C++标准)!(只是,它需要事先调用析构函数。)它还需要防止自我分配。像赫伯·萨特一样,我真的不喜欢这种技术;如果有人从holder_ref(可能不太可能)派生,那就太糟糕了。此外,请参见此处:@Charles:公平地说,GotW#23中提出的观点不适用于此处(而且我可以保证,由于图书馆的限制),除了第6点和第7点,我认为这是基于标准错误的迂腐行为(即,从技术上讲,与文字相符,但可能是无意的,并且普遍理解不同),这在实践中是不相关的,对于第4点来说,这是一个令人沮丧的问题。更正:第4点与我无关。在我的例子中,这个解决方案在技术上是合理的。您还可以将其实现为一种指针包装器,并重载
*
->
,而不是使用
get()
。。。
holder_ref& operator =(holder_ref const& other) {
    new (this) holder_ref(other);
    return *this;
}