【问题标题】:std::set<int * const> won't compilestd::set<int * const> 不会编译
【发布时间】:2019-06-25 14:35:16
【问题描述】:

这里的const是编译问题的原因。但是,我自己实现了 AVL 树,我不明白为什么。

代码如下:

#include <set>

int main ()
{
    int a;
    
    // I want the set to carry the "promise"
    // to keep the pointers constant
    std::set<int * const> x;
    
    x.insert(&a);
}

这是错误:

In file included from /usr/include/c++/7/string:48:0,
                 from /usr/include/c++/7/bits/locale_classes.h:40,
                 from /usr/include/c++/7/bits/ios_base.h:41,
                 from /usr/include/c++/7/ios:42,
                 from /usr/include/c++/7/ostream:38,
                 from /usr/include/c++/7/iostream:39,
                 from demo.cpp:1:
/usr/include/c++/7/bits/stl_function.h: In instantiation of ‘struct std::_Identity<int* const>’:
/usr/include/c++/7/bits/stl_tree.h:2091:29:   required from ‘std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(_Arg&&) [with _Arg = int* const; _Key = int* const; _Val = int* const; _KeyOfValue = std::_Identity<int* const>; _Compare = std::less<int* const>; _Alloc = std::allocator<int* const>]’
/usr/include/c++/7/bits/stl_set.h:510:48:   required from ‘std::pair<typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator, bool> std::set<_Key, _Compare, _Alloc>::insert(std::set<_Key, _Compare, _Alloc>::value_type&&) [with _Key = int* const; _Compare = std::less<int* const>; _Alloc = std::allocator<int* const>; typename std::_Rb_tree<_Key, _Key, std::_Identity<_Key>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator = std::_Rb_tree_const_iterator<int* const>; std::set<_Key, _Compare, _Alloc>::value_type = int* const]’
demo.cpp:11:18:   required from here
/usr/include/c++/7/bits/stl_function.h:877:7: error: ‘const _Tp& std::_Identity<_Tp>::operator()(const _Tp&) const [with _Tp = int* const]’ cannot be overloaded
       operator()(const _Tp& __x) const
       ^~~~~~~~
/usr/include/c++/7/bits/stl_function.h:873:7: error: with ‘_Tp& std::_Identity<_Tp>::operator()(_Tp&) const [with _Tp = int* const]’
       operator()(_Tp& __x) const

有没有一种“干净”的方式来做到这一点? (即,不是一种解决方法,比如为这种情况创建一个带有比较器的“指针类”)

【问题讨论】:

  • const int* != int* const
  • 是的。我知道吗?
  • 我指出这一点是因为 int* const 是一个无法更改的值,这意味着 std::set 的任何内部结构都不能复制构造或分配,这违反任何 STL 集合的要求。您想防止更改指向的数据,但存储无法更改的指针没有意义,因为您不直接使用内部结构。
  • 我很困惑,集合中的元素cannot be modified,这是您正在寻找的保证

标签: c++ pointers constants stdset


【解决方案1】:

这是一个简单的程序来演示您遇到的问题:

int main(int argc, char ** argv)
{
   int * const a = NULL;
   int * const b = NULL;

   b = a;   // error: cannot assign to variable 'b' with const-qualified type
}

请注意,更改int * const 变量的值是编译时错误,因为该变量被认为是只读的。

std::set内部也有同样的问题——需要修改指定类型的变量,如果指定类型是只读的,则不能这样做。

将类型更改为 const int * 可能是您想要做的,因为该类型允许在必要时覆盖指针(同时不允许修改它们指向的 ints)。

【讨论】:

  • set 必须将其元素存储在某处——例如在内部数组(或类似数组)中。随着set 的更新,它将需要在该内部数组中添加/删除项目。因此,数组中的项目必须是可修改的,否则set 将无法正常工作。
  • 我真的不明白你为什么需要const class_name* const,只需使用const_iterator。防止在std::set 内部更改指针对您的代码有何影响?
  • @ElliottSmith I want to be able to add/delete/find in log(n) time, but not edit. 恕我直言,这正是 set 所做的。为什么您不能将&amp;a 视为另一个数字?
  • @ElliottSmith 我不确定我是否理解您的评论——您能给我举个例子说明用户如何编辑 std::set 的元素吗? (AFAICT,您可以插入或删除元素,但不能就地修改它们,尽管我可能会遗漏一些东西)
  • 好的,指针是共享的,这很好。然后将它们按值复制到std::set 中,这仍然可以。如果他们是const int*,您确实无法更改该值,那么您的问题在哪里?我认为这里有一个基本的误解,你能否提供一个真实的案例场景,你需要它是const int* const INSIDE std::set
【解决方案2】:

您不能修改存储在std::set 中的元素,因此这一点没有实际意义。它旨在使元素保持排序顺序,而修改会破坏该保证。这就是迭代器(std::set&lt;T&gt;::iteratorstd::set&lt;T&gt;::const_iteratorboth return const references 的原因。

没有办法编辑缺少mutable(或const_cast)的元素,在这种情况下,您仍然需要保证顺序保持不变。

【讨论】:

  • 我愚蠢地忘记了 std::set 无论如何都会强制这种常量。谢谢你的回答。
  • 但是,如果你想使用 c++ 的 const 正确性特性,并且在某些时候你只有指向 T 的 const 指针,你将无法使用 std::set.
  • 您通常可以将const T 复制到Tint* 也不例外。
  • 最后一点:“它旨在使元素保持有序,而修改会破坏这一保证。”我只想说这不是完全正确的——你只需要保持键不变(任何影响 does 提供对整个元素的 only const 访问。只是技术性问题,但也许您可以编辑答案以澄清这一点?
  • @ElliottSmith 是的,我已经编辑了这种可能性,但我想保持最初的措辞不变,因为 mutableconst_cast 是规则的例外。
【解决方案3】:

更正式的回答是std::set满足成为AllocatorAwareContainer的要求:

一个集合满足一个容器的所有要求,一个 关联的可逆容器([container.requirements]) 容器([associative.reqmts]),和分配器感知容器 (表 65)

table 33 的 [allocator.requirements] 中,您可以阅读:

T, U, C any cv-unqualified object type ([basic.types])

其中 T 与 X::value_type 相同,其中 X 为 an allocator class for type T。这意味着std::allocator&lt;int * const&gt; 不符合上述要求。

这适用于许多其他容器,例如矢量,您可以在此处阅读更多内容:Does C++11 allow vector<const T>?

[编辑[

Visual Studio 给出了更多的描述性错误:

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\INCLUDE\xmemory0(585):错误 C2338:C++ 标准禁止 const 元素的容器,因为分配器格式不正确。

使用 clang,您可能会看到第一个错误行指向分配器标头:

../include/c++/5.5.0/ext/new_allocator.h:93:7: 错误:'address' 的多个重载实例化为相同的签名 '__gnu_cxx::new_allocator::const_pointer (__gnu_cxx::new_allocator ::const_reference) const noexcept' (又名 'int *const *(int *const &) const noexcept') 地址(const_reference __x) ........

【讨论】:

    猜你喜欢
    • 2013-11-23
    • 1970-01-01
    • 2014-05-12
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-18
    相关资源
    最近更新 更多