在类中内联

时间:2015-02-22 20:16:48

标签: c++ inline

我有一个模板化的类,其中大多数方法都在类中定义,因此可以内联它们。在下面的示例中,我展示了一个复制构造函数,但它可以是任何东西。

template <typename T>
class Vector {
private:
    ...
public:
    Vector(const Vector& v) {
        if (condition) {
            call_1(...);
        } else {
            call_2(...);
        }
    }
};

在这个构造函数中,condition几乎总是正确的,因此我想给编译器提供所有提示,以便它内联call_1而不是call_2(我想防止代码膨胀) 。我有两个问题:

  1. 首先,我考虑将call_1call_2定义为对象的方法,并在类中定义call_1,在类外定义call_2。还有什么我可以做的来帮助编译器吗?
  2. 如果我希望call_1call_2被许多不同的类共享,并因此将它们定义为类外的函数,我该怎么办?
  3. 感谢。

2 个答案:

答案 0 :(得分:1)

所以这有不同的方面。

  1. 一般代码膨胀 - 不经常执行的代码不应该作为内联代码添加到定期执行的常规代码路径中。
  2. 让编译器知道某些路径比其他路径更可能。
  3. 让我们先了解一般代码膨胀的细节。它会导致更大的可执行文件,这通常不是一件好事。但是性能开销相对较小 - 如果vector<T>.push_back的每个实例都包含相同的大量代码(在大型应用程序中,push_back,那么整个可执行文件的大小就更大了。可能被称为数千次[我的11k行编译器有70多个调用,并且它甚至没有使用向量],并且添加几十个指令当然会导致整个应用程序变得更大所以没有内联代码肯定有价值。

    第二个方面对于编制者做出正确决定的能力非常重要&#34;。简单地在类之外声明一个函数可能会有所帮助,但除非编译器也知道它不太可能发生,否则无法保证编译器不会内联反映异常情况的函数。

    但是,在技术方面,您唯一的便携式选择是将功能移出类定义。这是至少暗示编译器不适合内联的唯一方法。但是编译器有时会决定“无论如何都值得内联这些代码”,并且它是模板代码,你们不能真正做到“#34; let&#39; s将代码放在一个源代码文件中,该文件不与调用者一起编译。

    使用扩展到__builtin_expect(expr, likely)的宏,其中expr是您的条件,likely&#34;共同结果&#34; (0false1true。不幸的是,这在Microsoft编译器中不可用。您可能还想探索扩展为&#34;不内联的宏的选项&#34;例如GCC的__attribute__((noinline))强制代码不被内联。

    使用大多数高级编译器中提供的配置文件驱动的优化和相关注释,这将使编译器选择正确的选项。

    对于问题2,通常很难这样做 - 编写一些通用代码可能会起作用,例如:

    template<T>
    T* grow_allocation(T* existing, size_t cur_size, size_t new_size)
    {
        T* new_alloc = new T[new_size];
        // There is probably clever stuff like std::copy and std::move
        // that is "better" than this - writing basic loops to clearly
        // show what is being done.
        for(size_t i = 0; i < cur_size; i++)
        {
            new_alloc[i] = existing[i]; 
        }
        for(size_t i = cur_size; i < new_size; i++)
        {
            new_alloc[i] = T();
        }
        // Probably need to deal with "new_size < cur_size" and destroy
        // those too... 
    }
    

    现在,您可以从具有动态分配的内存区域的所有位置调用此函数来保存T类型的对象。 [这是可以轻易构建的]

答案 1 :(得分:0)

Regardin

  

我还能做些什么来帮助编译器吗?

如果在编译时知道condition,则可以使用两个构造函数:

Vector(const Vector& v, std::true_type) {
   call_1(...);
}

Vector(const Vector& v, std::false_type) {
   call_2(...);
}

关于

  

如果我希望call_1call_2被许多不同的对象共享并因此将它们定义为类外的函数,我该怎么办?

当你说许多不同的对象时,我认为你的意思是许多不同的类。我能想到的选择:

  1. 制作call_1call_2个全局函数,并从所有类中调用它们。如果使用它们的类不能被认为具有共同的基类型,则这种方法是有意义的。

  2. 创建一个公共基类并将函数放在基类中。从派生类中调用它们。只有在基类派生类关系有意义的情况下,这种方法才有意义。