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

问题描述

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

// 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<MyClass*>::size_type

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

doFoo2()std::vector<MyClass*>::size_type 传递给 foo(),因此 foo() 的第二个实现在这里非常有效。除了向调用者公开私有向量的实现细节之外,没有任何抱怨。最后我想我们需要include a separate header for MyClass

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

无论如何,谢谢你的包容。想法?

解决方法

当然,向量的大小不是 std::size_t 类型的可能性很小

这种可能性在这种情况下实际上并不存在,因为 std::vector<MyClass*>::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::size_t 是一种能够保存任何数组大小的类型,包括通过分配器分配的数组。

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

不能保证所有 std::vector<T>::size_typeT 都相同,但是您很难找到 std::vector 的实现,其中 size_type 并不总是std::size_t

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...