gpt4 book ai didi

c++ - 为什么不能将 const set 作为 const set 传递给函数?

转载 作者:太空狗 更新时间:2023-10-29 23:23:07 25 4
gpt4 key购买 nike

在这被标记为重复之前,我知道 this问题,但就我而言,我们谈论的是 const 容器。

我有 2 个类:

class Base { };
class Derived : public Base { };

还有一个函数:

void register_objects(const std::set<Base*> &objects) {}

我想调用这个函数:

std::set<Derived*> objs;
register_objects(objs);

编译器不接受这个。为什么不?该集合不可修改,因此不存在将非派生对象插入其中的风险。我怎样才能以最好的方式做到这一点?

编辑:
我知道现在编译器的工作方式 set<Base*>set<Derived*>完全无关,因此找不到函数签名。然而,我现在的问题是:为什么编译器会这样工作?不看会不会有异议const set<Derived*>作为 const set<Base*> 的导数

最佳答案

编译器不接受的原因是标准告诉它不要这样做。

标准告诉它不要这样做的原因是委员会没有引入规则 const MyTemplate<Derived*>const MyTemplate<Base*> 的相关类型即使非常量类型不相关。而且他们当然不想要 std::set 的特殊规则,因为通常语言不会为库类制定特殊情况。

标准委员会不想让这些类型相关的原因是 MyTemplate 可能没有容器的语义。考虑:

template <typename T>
struct MyTemplate {
T *ptr;
};

template<>
struct MyTemplate<Derived*> {
int a;
void foo();
};

template<>
struct MyTemplate<Base*> {
std::set<double> b;
void bar();
};

那么传递 const MyTemplate<Derived*> 到底意味着什么?作为const MyTemplate<Base*> ?这两个类没有共同的成员函数,并且布局不兼容。您需要两者之间的转换运算符,否则编译器不知道该怎么做,无论它们是否为 const。但是模板在标准中的定义方式,即使没有模板特化,编译器也不知道该做什么。

std::set它本身可以提供一个转换运算符,但只需要制作一个拷贝 (*),您自己可以轻松完成。如果有这样的东西 std::immutable_set , 那么我认为有可能实现这样一个 std::immutable_set<Base*>可以从 std::immutable_set<Derived*> 构建只需指向同一个 pImpl。即便如此,如果您在派生类中重载了非虚拟运算符,也会发生奇怪的事情——基础容器将调用基础版本,因此如果它有一个非默认比较器可以对集合进行任何操作,则转换可能会对集合取消排序对象本身而不是它们的地址。因此,转换会带来严重的警告。但无论如何,没有 immutable_set , const 与 immutable 不同。

此外,假设 DerivedBase 有关通过虚拟或多重继承。那么你不能仅仅重新解释 Derived 的地址作为 Base 的地址:在大多数实现中,隐式转换会更改地址。因此,您不能只是批量转换包含 Derived* 的结构。作为包含 Base* 的结构无需复制结构。但 C++ 标准实际上允许任何非 POD 类发生这种情况,而不仅仅是多重继承。和 Derived是非 POD,因为它有一个基类。因此,为了支持对 std::set 的更改,继承和结构布局的基础知识将不得不改变。这是 C++ 语言的一个基本限制,标准容器不能以您想要的方式重新解释,而且我不知道有什么技巧可以在不降低效率或可移植性或两者的情况下使它们如此。这令人沮丧,但这东西很难。

由于您的代码无论如何都是按值传递集合,因此您可以只复制该拷贝:

std::set<Derived*> objs;
register_objects(std::set<Base*>(objs.begin(), objs.end());

[编辑:您已将代码示例更改为不按值传递。我的代码仍然有效,除了重构调用代码以使用 std::set<Base*> 之外,afaik 是你能做的最好的。首先。]

std::set<Base*> 编写包装器确保所有元素都是 Derived* ,Java 泛型的工作方式,比安排您想要高效的转换更容易。所以你可以这样做:

template<typename T, typename U>
struct MySetWrapper {
// Requirement: std::less is consistent. The default probably is,
// but for all we know there are specializations which aren't.
// User beware.
std::set<T> content;
void insert(U value) { content.insert(value); }
// might need a lot more methods, and for the above to return the right
// type, depending how else objs is used.
};

MySetWrapper<Base*,Derived*> objs;
// insert lots of values
register_objects(objs.content);

(*) 实际上,我猜它可以写时复制,在以典型方式使用 const 参数的情况下,这意味着它永远不需要进行复制。但是写时复制在 STL 实现中有点名誉扫地,即使不是这样,我怀疑委员会会想要强制执行这样一个重量级的实现细节。

关于c++ - 为什么不能将 const set<Derived*> 作为 const set<Base*> 传递给函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1118807/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com