std::size_t 与 size_type 作为参数和函数 return 类型
std::size_t vs. size_type as parameters and function return types
假设我有这段代码。哪种方法更好?
// 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
类型。也就是说,我们只能依靠软件分析工具来告诉我们向量大小的 return 类型发生了变化。
第二种方式向调用者公开向量的实现细节。这会破坏封装吗?我不知道,你告诉我!
这是另一个更复杂的例子。哪种方法更好?
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()
关心的唯一向量about 是包含 MyClass*
类型元素的向量。现在,作为一个学术问题,std::vector<NOTMYCLASS*>::size_type
是否总是与 std::vector<MyClass*>::size_type
相同?我其实不知道这个问题的答案,但我一直在听到 'yes' and 'no'。最后,再次出现封装问题(如果这是一个问题)。
无论如何,谢谢你的包容。想法?
std::size_t
是一种能够容纳任何数组大小的类型,包括通过分配器分配的数组。
这意味着 std::size_t
将始终能够存储 std::vector<T>::size()
的结果,因此方法 1 永远不会导致溢出并且完全可读。
无法保证 std::vector<T>::size_type
对所有 T
都相同,但您很难找到 std::vector
的实现,其中 size_type
是并不总是 std::size_t
.
but of course there's the off-chance that the size of the vector isn't of type 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();
};
假设我有这段代码。哪种方法更好?
// 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
类型。也就是说,我们只能依靠软件分析工具来告诉我们向量大小的 return 类型发生了变化。
第二种方式向调用者公开向量的实现细节。这会破坏封装吗?我不知道,你告诉我!
这是另一个更复杂的例子。哪种方法更好?
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()
关心的唯一向量about 是包含 MyClass*
类型元素的向量。现在,作为一个学术问题,std::vector<NOTMYCLASS*>::size_type
是否总是与 std::vector<MyClass*>::size_type
相同?我其实不知道这个问题的答案,但我一直在听到 'yes' and 'no'。最后,再次出现封装问题(如果这是一个问题)。
无论如何,谢谢你的包容。想法?
std::size_t
是一种能够容纳任何数组大小的类型,包括通过分配器分配的数组。
这意味着 std::size_t
将始终能够存储 std::vector<T>::size()
的结果,因此方法 1 永远不会导致溢出并且完全可读。
无法保证 std::vector<T>::size_type
对所有 T
都相同,但您很难找到 std::vector
的实现,其中 size_type
是并不总是 std::size_t
.
but of course there's the off-chance that the size of the vector isn't of type 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();
};