【问题标题】:Efficiency of member function returning std::vector.size() in object oriented framework面向对象框架中返回 std::vector.size() 的成员函数的效率
【发布时间】:2026-02-01 01:55:02
【问题描述】:

我在prob.h 中声明了一个类,因此:

struct A_s{
    int a, b;
}

class A_c{
    private:
        std::vector<A_s> vec_of_A_s;
    public:
        int vec_of_A_s_size() const{return static_cast<int>(vec_of_A_s.size());}
}

使用A_c A;//A is an object of class A_c,在我的实现.cpp 文件的其他地方,我有以下行:

for(int i = 0; i &lt; A.vec_of_A_s_size(); i++) {...//do loop stuff}

我从我的程序设计中知道A.vec_of_A_s_size() 是循环不变的。但是,我真的很想避免以下情况(很麻烦):

int sz = A.vec_of_A_s_size();
for(int i = 0; i < sz; i++) {...//do loop stuff}

我能否充分且始终如一地依赖一个编译器,即启用了优化 (-O2) 的发布版本不会每次都评估 vec_of_A_s.size()

这是我已经尝试过的问题:

(1)(请参阅下面的编辑更新)即使是调试版本,带有选项-fPIC -fno-strict-aliasing -fexceptions -g -std=c++14,查看反汇编程序输出,vec_of_A_s.size() 也只评估一次。但是,编译器会始终如一地可靠地进行这种优化吗?有任何已知的例外吗?我的怀疑和需要保证的部分原因源于下面的问题 (2)。

(2)我查看了关于 SO:Performance issue for vector::size() in a loop 的相关问题。那里的问题直接评估循环中向量的大小,如下所示:

for(int i = 0; i &lt; vec_of_A_s.size(); i++) {...//do loop stuff}

就我而言,向量不能直接访问。它是A_c 的私有成员,其大小只能通过公共成员函数A.vec_of_A_s_size() 访问。因此,在 for 循环中必须发生额外的间接/重定向层。该线程上的答案似乎表明编译器确实会优化循环不变量。但是在向量的大小不能直接和公开获得的情况下(如上),编译器能否可靠地保证循环不变优化?

(3)在关于此类问题的其他相关问题中,一个常见的答案似乎是对程序进行剖析。如果以及当我进行分析时,我究竟应该寻找什么来验证这个特定的优化?该代码是更大的数值分析代码的一部分,这绝对不是当前的瓶颈。然而,很高兴知道如何在分析器中验证这一点。如果这个问题 (3) 太宽泛,我们深表歉意。我对分析比较陌生。但是分析器是否允许分析单个函数,比如上面包含for 循环的函数?这样,我可以确定与此功能有关的瓶颈在哪里。


编辑更新:

在 (1) 上,使用上述编译器选项进行调试构建优化循环不变量是不正确的。我错了。深入挖掘,发现该函数确实被调用了两次。

【问题讨论】:

  • C++ 编译器可能会内联您认为在 (2) 中的间接。所以实际上可能不需要付出任何代价。
  • 回答(3),如果不是瓶颈,何必呢?如果您使用像 gprof 这样的统计分析器,您可能永远不会看到调用的代码。所以我不会太担心。
  • size() 的调用可能是内联的并且可能只执行一次。但我在标准中没有看到任何保证你的东西。但是,如果您使用 range-for 循环,那么您保证 .size() 只会被评估一次。
  • @KlaasvanGend 声明中定义的任何成员函数(即头文件)本身不是自动内联的吗?您的评论表明即使这样也不能保证。
  • @JesperJuhl en.cppreference.com/w/cpp/language/range-for 中的示例似乎仅适用于可公开访问的原始向量/数组。是否可以通过 OP 中指定的成员函数使用 range-for?

标签: c++ optimization vector loop-invariant


【解决方案1】:

如果你的循环在向量上调用了一个非常量的方法,那么我几乎可以肯定地说所有的赌注都没有了。

如果您只在向量上调用 const 方法,那么您可能希望得到优化,但由于标准不要求它们,您不能真正责怪编译器没有进行对您来说可能很明显的优化,

鉴于您可以在 for 循环中声明多个变量,只要它们属于同一类型,将 sz 带入循环似乎是显而易见的事情。或者,您可以向后运行循环吗?

【讨论】:

  • 是的,循环中发生的事情不会改变向量。但只有我知道。编译器不需要。因此,似乎无法保证优化。我不可能向后运行循环——我知道这有什么帮助。如果我们继续前进,似乎sz 是唯一有保障的方式。
  • @Tryer:的确,这就是它的要点。