【问题标题】:How to implement own function to std::vector?如何实现自己的函数到 std::vector?
【发布时间】:2011-06-15 17:10:47
【问题描述】:

我想添加一个将 .size() 值作为整数而不是无符号整数返回的函数。

编辑:由于cmets,我解释的更详细:

我有代码:

int something = 3;
if(arr.size() > something) 

这会产生编译器警告,我不喜欢将 (int) 添加到我拥有它的每个地方。 所以,我认为有一个 sizei() 函数会很好的解决方案:

int something = 3;
if(arr.sizei() > something) 

不会产生警告。

所以,我不想创建一个单独的函数,而是在 std::vector 本身中创建一个函数。

编辑:似乎唯一的方法是创建另一个函数,例如:

template <typename T>
inline int sizei(const T &arr){
    return (int)arr.size();
}

积极的一面:这似乎根本不会增加我的可执行文件大小。

【问题讨论】:

  • 为什么?计数为 -1 的用例是什么?
  • 我不确定这个问题是否有意义。您对返回的类型有什么具体问题吗?
  • 这个问题被不合理地否决了。一些编译器会在混合有符号无符号比较时发出警告,而我发现的无符号数的唯一好用例是定义明确的模运算。使用无符号来限制域(几乎)总是错误的做法,IMO。
  • 当然你不应该比较混合符号,但是如果出现这种情况你应该在你需要的地方转换size()的结果,即转换。这样您就可以定位问题并且不需要更改向量的语义以及“大小”的固有无符号性质。
  • 哦,如果你想激怒那些告诉你的人,请提醒他们不要使用size_t,而应该使用std::vector&lt;whatever&gt;::size_type ;-)

标签: c++ std


【解决方案1】:

首先,您为什么要这样做?我没有看到任何理由或优势:

无论如何,你可以这样做:

template<typename T>
int size(const std::vector<T> &v) { return (int) v.size(); }

//use
std::vector<int> ints;
//...
int count = size(ints);

我仍然认为这样做没有任何意义。你可以简单地写:

int count = (int) ints.size(); 

但我仍然会说它并不比以下更好:

size_t count = ints.size(); //don't prefer anything over this. Always use size_t

建议:避免使用int 作为大小。首选size_t


至于您问题中的编辑。为什么不使用size_t 作为:

size_t something = 3;
if(arr.size() > something) 

没有警告。在我看来,如果您在整个程序中始终如一地选择数据类型,您就不会遇到将 int 与定义为 unsigned 整数类型的 size_t 进行比较的情况.

或者,如果您必须使用一些遗留代码,并且使用 int 来确定大小,那么我认为在需要时使用显式转换更好,而不是在框架本身中添加函数,这隐藏了潜在的问题:

int something = /*some legacy code API call or something */;
if(arr.size() > (size_t) something) 

//or even better;
size_t something = (size_t) /*some legacy code API call or something */;
if(arr.size() > something) 

【讨论】:

  • @littleadv 我认为这正是他所要求的:“一个将 .size() 值作为整数返回的函数”
  • why would you want to do this? 这不是一个不常见的要求:例如:您可能有一个函数可以返回某种结构化存储中特定命名“对象”的大小。如果对象缺失,则返回-1,因为我们需要区分缺失数据和空数据。此外,这些文件中丢失的数据不足以证明抛出的“异常”)。所以,你经常会得到类似if (object.dataSize("Foo") == sizeof(MyStruct)) { readIt(); } 这样的代码,这会导致编译器警告比较有符号和无符号。
  • @Nawaz:好的,删除了反对票。但我不会投赞成票,因为这种将无符号类型用于数字 的想法是恕我直言,社会群体的想法是最糟糕的。这就像一种宗教信仰。每个人都知道这很疯狂,但也(错误地)认为大多数其他人都认为事情就是这样。并向外表达他/她认为该团体所拥有的信念。但是,没有任何优势,甚至经常引用的助记符也没有(比如 cmets,但 typedef 涵盖了这一点)。并且有很多极端的缺点。无数次浪费的(wo)工时。
  • 对于使用unsigned 的坏建议,我很想给-1。 unsigned,在 C++ 中,不能很好地工作,并且会导致许多问题。除非您进行按位运算或需要它的模属性,否则最好避免使用它。 (我不知道这种使用无符号类型的狂热从何而来。我没有在任何更好的教程作品中看到它。)
  • +1 用于将size_t 用作vector 不能有负数(至少在这个宇宙中没有)
【解决方案2】:

通常,在 C 和 C++ 中,您永远不应该使用诸如 size_t 之类的无符号类型来限制域。这是因为 (1) 这些语言不提供范围检查,并且 (2) 它们确实提供了不合理的隐式提升。没有范围检查意味着 (1) 没有优势,不合理的隐式促销意味着 (2) 非常不利的劣势,所以这样做很愚蠢:没有优势,非常不利的劣势。

但是,这些语言的标准库可以做到这一点。他们这样做只是出于历史原因,不可逆转地陷入了曾经有意义的早期决策中。这会产生极其愚蠢的后果,例如 C99 需要 17 (!) 位来表示 ptrdiff_t,并且会产生上述极其不良的后果,例如花费大量时间来寻找由隐式提升(等)导致的错误。例如,在 C++ 中,您实际上可以保证 std::string( "bah!" ).length() &lt; -5 - 这很容易让您绊倒,而且无论如何设计都是愚蠢的。

现在,你不能在std::vector 中注入新的成员函数,但你可以添加一个独立的函数。一个好名字是countOf。将其模板化,以便它可以应用于几乎任何东西(原始数组、向量等)。

据我所知,startOfendOfcountOf 三个函数是由 Dietmar Kuehl 首次确定的。 C++0x 将有 std::beginstd::end,但 AFAIK 没有对应的 std::size。同时,您可以定义这种支持,它允许您将任何类型的容器和原始数组一视同仁。

my blog 提供了示例实现和进一步讨论。


编辑添加一些代码,因为它是在 cmets 中请求的。

检测合适的迭代器类型:

template< typename Type >
struct It
{
    typedef typename Type::iterator T;
};

template< typename Type >
struct It< Type const >
{
    typedef typename Type::const_iterator T;
};

template< typename ElemType, Size N >
struct It< ElemType[N] >
{
    typedef ElemType* T;
};

还有countOfstartOfendOf 函数,使用推导的迭代器类型:

template< typename T >
inline Size countOf( T const& c )           { return static_cast<Size>( c.size() ); }

template< typename T, Size N >
inline Size countOf( T (&)[N] )             { return N; }

template< typename T >
inline typename It<T>::T startOf( T& c )    { return c.begin(); }

template< typename T, Size N >
inline T* startOf( T (&a)[N] )              { return a; }

template< typename T >
inline typename It<T>::T endOf( T& c )      { return c.end(); }

template< typename T, Size N >
inline T* endOf( T (&a)[N] )                { return a + N; }

其中Sizeptrdiff_t 的类型定义。

注意:在 64 位 Windows 中,int(甚至long)是 32 位的。因此,int 通常不足以用于非常大的数组。 ptrdiff_t 保证能够表示任意两个指针之间的差异,前提是该差异已明确定义。


干杯&hth。

【讨论】:

  • “曾经有意义的早期决定”——它们曾经有意义吗?如果在有符号和无符号类型之间没有比较运算符(可能你也不需要混合算术,只是显式转换),那么我可以看到在标准库中使用无符号类型的意义。但是,就目前的情况而言,您不能只使用有符号类型,不能只使用无符号类型,也不能安全地混合使用它们。正如你所说,这有点束缚。
  • 我认为你的类型尊重它们所代表的基本属性是很重要的。例如,谈论两种尺寸之间的差异是非常有意义的,而这种差异的标准公式是abs(size1, size2)。如果这不适用于您用于尺寸的类型,那么您使用的类型有误。
  • @James:但相反,如果您用于索引的类型不允许您谈论 signed 差异,那么您使用的是错误的类型。所以你不能对大小和索引使用相同的固定宽度类型,也不能使用一对对应的有符号/无符号类型,因为它们不适合,你不能有有符号的表示有符号的差异在无符号的范围内无符号的表示有符号范围的绝对差异。在 C 和 C++ 中引入 bigint 内置类型,我们可以进行合理的算术运算,但我们会失去那种接近金属的氛围。
  • @Steve:我知道无符号大小类型有意义的唯一具体示例是 16 位 x86 编程。然后其他选项可能是 32 位有符号类型,但会导致效率低下,或者是字符串大小限制为 32KB 的 16 位有符号类型(可以想象,可能需要一个 48KB 的字符串!)。这个难题反映在 ptrdiff_t 的 17 位 C99 要求上,它是有符号的并且需要能够表示任意两种大小之间的差异。在较早的有关此问题的讨论中,在 Usenet 上,我的立场是可以有不同的规则,例如嵌入。
  • @Alf:如果ptrdiff_t 不必表示size_t 类型的任何两个值之间的差异,而只是表示实际对象的任意两个大小之间的差异,那又会怎样呢?然后,您将对所有内容使用签名类型。这两种差异都适用于真实对象和指针,它们只是不适用于类型(因此 James 仍然没有理解他的观点,“类型尊重它们所代表的基本属性”)。 distance(INT_MAX, INT_MIN) 要么未定义,要么被定义为一个荒谬的值(环绕),但很高兴并不代表真正的距离。
【解决方案3】:

你可以从向量中导出如下:

template<typename T>
class my_vector : public vector<T>
{
  // missing constructors!

  int size() const
  {
    if (vector<T>::size() > INT_MAX)
      throw std::range_error("too many elements in vector");
    return (int) vector<T>::size();
  }
};

缺点是您必须自己定义和转发构造函数。

【讨论】:

  • 缺少构造函数,缺少虚拟析构函数...灾难的秘诀。
  • 我写道缺少构造函数。至于虚析构函数,它们将继承自 std::vector,如果有的话。
  • @zvrba:没有虚拟析构函数,这正是问题所在。如果你 delete 你的 my_vector 类从一个指向基的指针,你就完蛋了。不,告诉自己不要这样做并不能阻止它发生。
  • @rubenvb:“告诉自己不要这样做并不能阻止它的发生”我不确定你为什么要动态分配这个类,但如果你这样做了,那么使用 @ 987654324@ 可以防止任何不好的事情发生,因为shared_ptr 不需要虚拟析构函数来使其正确。如果没有shared_ptr,你如何管理这些动态分配的向量?
  • @rubenvb:是的,“你不能多态地处理它们”的说法有点误导。你不能用它们做一件事,那就是多态delete。但是对于容器,以及像这样只添加非虚拟函数的派生类,在我看来,您根本不可能使用多态性,更不用说多态性删除。 shared_ptr 可以为你伪造。 IMO 不这样做的真正原因是希望一切都成为成员函数是徒劳的,而它很可能是非成员,但这正是提问者所要求的。
【解决方案4】:

我倾向于使用显式转换为 int 而不是函数:static_cast&lt;int&gt; (v.size())。更好的办法是在处理内存大小时始终使用size_t。例如,支持for (size_t i=0; i &lt; v.size(); ++i) 而不是for (int i=0; i &lt; (int) v.size(); ++i)。为工作使用正确的类型。您不应该将 std::vector 大小与有符号类型进行比较。

请参阅以下参考资料,了解为什么您应该更喜欢 size_t 而不是 int:

【讨论】:

  • 请通过vector_size中的const引用获取向量
  • 我仍然不清楚为什么我不应该将有符号类型与 std::vector 大小进行比较...我已经多次解决了这个有符号/无符号问题,并且我学会了只使用整数对于一切,在极少数情况下,我在真正需要时使用无符号整数。
  • @Rookie:不进行比较的原因是,如果您的有符号值为负,它将(可能)转换为一个非常大的无符号值,并且比较结果会声称当向量真的更大时,它会更小。如果你知道你的整数值真的永远不会是负数,你不妨使用无符号类型,因为它更容易使用标准库。 C++ 容器根本无法很好地满足您对带符号类型的偏好:编译器警告告诉您通过显式转换来弥合差距。
【解决方案5】:

.size() 的快速答案是:不。对于向量,可能是它的存储值和分配方法(默认new/delete,通常不会被覆盖)以及使用InputIterator 的方法。

大多数人会问你为什么想要一个不同的size_t。如果只是烦人的警告,您可以强制转换或使用无符号整数来迭代/检查 size()。 (如果代码很多,您将不得不查找/替换)...如果它正在处理空条件,您可以将向量包装在一个具有一些智能的类中。顺便说一句,由于我不知道您手头的问题,因此寻找想法和已实现功能的好地方是 std 库的算法,例如 sortfor_eachfind 等等。

std算法见:http://www.sgi.com/tech/stl/table_of_contents.html

【讨论】:

  • 我打字很慢 :) ...但我猜每个人都会说为什么?我说跟着纳瓦兹的回答去
【解决方案6】:

虽然我认为@Nawaz 提供了最合适的答案,但如果您真的想向 std::vector 添加一个额外的成员,那是不可能的。 @zvrba 提供了唯一可以完成的方法,但正如 cmets 中所述,std 容器类型没有虚拟析构函数,因此不打算从中继承。

但是,您可以使用容器适配器实现一种新型矢量,如下所示:

template <class T>
class my_vector
{
public:
   int size_i() const
   {
      return static_cast<int>(container_.size());
   }

private:
   std::vector<T> container_;
};

这里的缺点是您必须显式公开您实际需要支持的容器的功能。如果您在整个代码中通常使用“std::vector”,这可能是一个重大变化。有关容器适配器的实现示例,请参见“std::queue”。

【讨论】:

  • 您可以通过方法通过引用公开包含的向量。例如std::vector&amp; operator()() { return container_; },然后像这样使用它:arr().push_back(foo);
  • 好点@Judge Maygarden,更进一步,您可能会实现一个隐式转换运算符 std::vector 并继续在代码中使用 my_vec (大部分)未修改。
猜你喜欢
  • 2018-04-19
  • 2015-02-20
  • 1970-01-01
  • 1970-01-01
  • 2017-09-08
  • 1970-01-01
  • 1970-01-01
  • 2019-04-23
  • 1970-01-01
相关资源
最近更新 更多