std::size_t 与 size_type 作为参数和函数返回类型

Posted

技术标签:

【中文标题】std::size_t 与 size_type 作为参数和函数返回类型【英文标题】:std::size_t vs. size_type as parameters and function return types 【发布时间】:2021-04-11 16:09:49 【问题描述】:

假设我有这个代码。哪种方法更好?

// Method 1
std::size_t size()

  // m_myVector is of type std::vector<MyClass*> in all examples
  return m_myVector.size();


// Method 2
std::vector<MyClass*>::size_type size()

  // m_myVector is of type std::vector<MyClass*> in all examples
  return m_myVector.size();

第一种方法适用于 99% 的情况,但当然也有可能向量的大小不是 std::size_t 类型。也就是说,我们可以只依靠软件分析工具告诉我们向量大小的返回类型发生了变化。

第二种方式将向量的实现细节暴露给调用者。这会破坏封装吗?我不知道,你告诉我!

这是另一个更复杂的例子。哪种方法更好?

void doFoo(const SomeClass& someObject)

  // These could be ints or size_types... Feel free to use your imagination
  std::size_t firstCount = someObject.getFirstCount();
  for (std::size_t i = 0U; i < firstCount; ++i)
  
    foo(firstCount);
  


void doFoo2(const SomeClass& someObject)

  // I thought I'd provide another example to help your imagination :)
  std::vector<MyClass*>::size_type secondCount = someObject.getSecondCount();
  for (std::vector<MyClass*>::size_type i = 0U; i < secondCount; ++i)
  
    foo(secondCount);
  


void doFoo3(const SomeClass& someObject)

  // The correct styling would be: NotMyClass*
  // But I really wanted to emphasize this was different from above, so I ALL CAPPED IT
  std::vector<NOTMYCLASS*>::size_type thirdCount = someObject.getThirdCount();
  for (std::vector<NOTMYCLASS*>::size_type i = 0U; i < thirdCount; ++i)
  
    foo(thirdCount);
  


// Method 1
void foo(std::size_t index)

  // m_myVector is of type std::vector<MyClass*> in all examples
  m_myVector.at(index)->doLotsOfStuff();


// Method 2
void foo(std::vector<MyClass*>::size_type index)

  // m_myVector is of type std::vector<MyClass*> in all examples
  m_myVector.at(index)->doLotsOfStuff();

好的,这是一个很长的例子,所以我将解释发生了什么。 doFoo()doFoo2()doFoo3() 都调用 foo(),在第一个实现中接收 std::size_t,在第二个实现中接收 std::vector&lt;MyClass*&gt;::size_type

doFoo()std::size_t 传递给foo(),因此foo() 的第一个实现在这里更有意义,但是我们索引一个std::vector&lt;MyClass*&gt;,它期待一个std::vector&lt;MyClass*&gt;::size_type。如果 size_type 未定义为 std::size_t,则不是特别好。

doFoo2()std::vector&lt;MyClass*&gt;::size_type 传递给foo(),所以foo() 的第二个实现在这里工作得很好。除了将私有向量的实现细节暴露给调用者之外,没有其他抱怨。最后我想我们需要include a separate header for MyClass

doFoo3()std::vector&lt;NOTMYCLASS*&gt;::size_type 传递给foo()... 而foo() 的实现都没有预料到这一点,因为foo() 关心的唯一向量是包含MyClass* 类型元素的向量.现在,作为一个学术问题,std::vector&lt;NOTMYCLASS*&gt;::size_type 总是与std::vector&lt;MyClass*&gt;::size_type 相同吗?我实际上不知道这个问题的答案,但我一直在听到'yes' and 'no'。最后,再次出现封装问题(如果是问题的话)。

无论如何,感谢您对我的包容。想法?

【问题讨论】:

对于定义了 size_type 的容器,使用它。否则,请使用size_t 我不喜欢宣传SomeClass 中有vector,除非我必须这样做。这是一个更抽象的合理时间,将using size_type = &lt;whatever&gt;; 添加到SomeClass,因此没有人知道或关心。此外,您几乎可以随时更改支持类型,而无需客户参与。 老实说,实际上size_type 总是与size_type 相同。 为什么不使用 auto,它 100% 的时间都有效。 @NathanOliver C++98 :) 【参考方案1】:

但当然也有可能向量的大小不是 std::size_t 类型的

在这种情况下实际上不存在这种可能性,因为std::vector&lt;MyClass*&gt;::size_type 是(间接保证并且必须是)std::size_t 类型。在这种情况下使用std::size_t 很好,并且不会泄露不必要的实现细节。


在标准容器的情况下,Container::size_type 直接基于正在使用的分配器定义。因此,使用size_type 通常仅在分配器类型(或容器类型本身)被模板化时才需要。在分配器的情况下,您可以使用分配器特征而不是容器成员类型,这允许您隐藏容器类型。如果容器类型本身是模板化的,那么隐藏它是没有意义的,因为只有知道容器的人才能首先实例化模板。

此外,您可以通过创建类型别名成员来隐藏 - 或者更确切地说混淆(以积极的封装方式) - 函数声明,就像 std::vector 具有基于其分配器的类型别名成员一样。

例子:

template<class Alloc>
class Foo

    // could be hidden with PIMPL if desired
    std::vector<MyClass*, Alloc> m_myVector;

public:
    // Since C++11
    using size_type = typename std::allocator_traits<Alloc>::size_type;
    // Prior to C++11
    typedef typename Alloc::size_type size_type;
    
    size_type size();
;

【讨论】:

关于std::vector&lt;MyClass*&gt;::size_typestd::size_t 类型的优点。为了补充答案,我发现引用标准的另一个答案:***.com/a/4849780/5849965【参考方案2】:

std::size_t 是一种能够容纳任何数组大小的类型,包括通过分配器分配的数组。

这意味着std::size_t 将始终能够存储std::vector&lt;T&gt;::size() 的结果,因此方法1 永远不会导致溢出并且完全可读。

无法保证std::vector&lt;T&gt;::size_type 与所有T 相同,但您很难找到std::vector 的实现,其中size_type 并不总是std::size_t

【讨论】:

请注意,虽然这对于向量是正确的,但对于其他容器不一定是正确的。我有一个虚拟化文件内容的类型,即使在 32 位构建中,容器的 size_type 也是 64 位类型(我想如果我要将它移植到不支持大于 4gb 的文件的系统,那么应该更改)。 我认为std::vector&lt;T&gt;::size_type必须和std::allocator&lt;T&gt;::size_type一样,不是吗?保证为std::size_t。只有当您处理std::vector&lt;T, MyAllocator&gt; 时,您才必须处理边缘情况。 @MooingDuck 该标准只要求size_type 是无符号的并且具有比difference_type 更大的最大值。 libstdc++ 似乎总是使用size_t,而libc++ 和MSVC++ 的STL 似乎使用std::allocator_traits&lt;Allocator&gt;::size_type,但无论哪种方式,大小仍然保证适合size_t

以上是关于std::size_t 与 size_type 作为参数和函数返回类型的主要内容,如果未能解决你的问题,请参考以下文章

<cstdint> 与 std::size_t 类型

“std::size_t”在 C++ 中有意义吗?

size_t 和 std::size_t 之间的区别

没有匹配函数调用‘std::set<unsigned int>::insert(std::size_t&)

将 opengl Window 转换为 std::size_t 并再次在 Linux 中有效,但在 OS X 中无效

如何定义自定义跨平台 size_t 类型?