具有 const 成员的结构向量

Posted

技术标签:

【中文标题】具有 const 成员的结构向量【英文标题】:Vector of structs with const members 【发布时间】:2021-06-17 10:15:35 【问题描述】:

多年后我又回到了 C++,而且我使用的是 C++17 标准。根据this question,似乎具有 const 成员的自定义结构并不总是与没有公共复制分配构造函数的向量兼容。

在我的情况下,与链接的问题不同,以下编译得很好:

#include <vector>

struct Foo  const int m_Bar; ;

int main()

    std::vector<Foo> vecFoos Foo1 ;
    vecFoos.push_back( Foo2 );
    return 0;

以下不是:

#include <vector>

struct Foo  const int m_Bar; ;

int main()

    std::vector<Foo> vecFoos Foo1 ;
    vecFoos.assign( Foo2 );  // using assign instead of push_back
    return 0;

它失败了:

$ g++ --std=c++17 main.cpp  && ./a.out 
In file included from /usr/include/c++/9/vector:60,
                 from main.cpp:1:
/usr/include/c++/9/bits/stl_algobase.h: In instantiation of ‘static _Tp* std::__copy_move<_IsMove, true, std::random_access_iterator_tag>::__copy_m(const _Tp*, const _Tp*, _Tp*) [with _Tp = Foo; bool _IsMove = false]’:
/usr/include/c++/9/bits/stl_algobase.h:404:30:   required from ‘_OI std::__copy_move_a(_II, _II, _OI) [with bool _IsMove = false; _II = const Foo*; _OI = Foo*]’
/usr/include/c++/9/bits/stl_algobase.h:441:30:   required from ‘_OI std::__copy_move_a2(_II, _II, _OI) [with bool _IsMove = false; _II = const Foo*; _OI = Foo*]’
/usr/include/c++/9/bits/stl_algobase.h:474:7:   required from ‘_OI std::copy(_II, _II, _OI) [with _II = const Foo*; _OI = Foo*]’
/usr/include/c++/9/bits/vector.tcc:321:29:   required from ‘void std::vector<_Tp, _Alloc>::_M_assign_aux(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = const Foo*; _Tp = Foo; _Alloc = std::allocator<Foo>]’
/usr/include/c++/9/bits/stl_vector.h:793:2:   required from ‘void std::vector<_Tp, _Alloc>::assign(std::initializer_list<_Tp>) [with _Tp = Foo; _Alloc = std::allocator<Foo>]’
main.cpp:8:30:   required from here
/usr/include/c++/9/bits/stl_algobase.h:382:39: error: static assertion failed: type is not assignable
  382 |    static_assert( __assignable::type::value, "type is not assignable" );

在一个不太简单的实际示例中,我尝试将一些具有 const 成员的结构重新分配给向量,但我遇到了这个问题,尽管编译器会抱怨不同的消息:

error: cannot bind rvalue reference of type ‘std::optional<long unsigned int>&&’ to lvalue of type ‘const std::optional<long unsigned int>’

在同一个输出中有不少:

error: non-static const member ‘const uint64_t myStruct::m_MyMember’, can’t use default assignment operator
error: no matching function for call to ‘std::optional<long unsigned int>::operator=(const std::optional<long unsigned int>&) const’

这三个有关系吗?我只是想了解为什么我可以将新元素重新分配给现有向量,以及这是否仅仅是因为我的结构没有显式的复制分配运算符。我怀疑答案是肯定的,因为添加我自己的复制分配构造函数确实绕过了示例代码中的问题:

Foo& operator=(Foo other)  return *this; 

但这似乎很愚蠢,我只是想找到一种明智的方法将这些重新分配给向量。

【问题讨论】:

你可以用std::unique_ptr 包裹你的元素。不过这会改变语法。 它不仅与向量有关,foo1 = foo2 分配也不起作用,vector::assign 正在尝试有效并重新分配给现有对象。如果你不太关心这样的效率,你可以简单地用vecFoos = std::vector&lt;Foo&gt;( Foo2 );重新分配向量 【参考方案1】:

const 成员的问题在于它们禁用了编译器生成的operator=。因此,如果您以不需要operator= 的方式使用向量,或者以某种方式提供自己的operator=,则一切正常。通常,您从不严格需要 const 成员字段,它们会给 STL 容器带来很多问题。最好避开它们。

【讨论】:

这是最简洁的答案。致 OP:如果您真的想保护成员数据,只需将它们设为私有并让 getter 函数返回 const。 @Nobilis 如果你所说的“优化”是指“不是为了通用而低效” @Nobilis 不,这不是优化。如果类型不可复制分配,分配根本不起作用。这是关于语义,而不是性能。 @Nobilis 它们必须是可复制分配的,因为您尝试复制分配它们。这与性能完全无关。如果您将对象放入向量中,并且从不做任何导致它们被复制分配的事情,您将不会注意到任何差异。 @AyxanHaqverdili 对,有道理,谢谢。【参考方案2】:

但这似乎很愚蠢,我只是想找到一种明智的方法将这些重新分配给向量。

你不能分配给const int任何地方,为什么数据成员会很特别?这就是const 的意思

你可以构造Foos,它允许你push_back,因为这只需要CopyInsertable,而不是任何可分配的。

assign 需要CopyAssignable,这不是绝对必要的,但暗示实现应该重用现有元素。

【讨论】:

但是我分配给不是 const 的向量而不是结构的 const 成员,对吗?所以至少从表面上看,我似乎应该能够用新的结构填充我的向量,并且它们是否具有 const 成员与向量本身无关。但听起来这是一个不正确的假设。 "a.assign(i,j) 前提条件:T 是 Cpp17EmplaceConstructible 到 X*i 和可分配从 *i。"来自container requirements 即现有元素可以重复使用

以上是关于具有 const 成员的结构向量的主要内容,如果未能解决你的问题,请参考以下文章

如何找到特定成员具有特定值的第一个结构?

为啥在向量类中实现 operator= 时返回 const 引用

如何在 const 成员函数中使用非常量成员函数?

跨越结构向量的成员

C语言 结构体声明中const成员如何初始化

如何访问和存储向量类型的结构成员?