Имеет ли значение, где указано ключевое слово inline, базовый класс, производный класс или оба? - PullRequest
2 голосов
/ 27 марта 2019

Предположим, мы не хотим указывать компилятору встроенную функцию-член, где это применимо.

class Base
{
 public:
      inline virtual f() = 0;
};

class Derived : public Base
{
 public:
      virtual f() override; // no inline specifier here
};

Нужно ли указывать inline в Derived::f() или я могу опустить ключевое слово и убедиться, чтоvirtual Derived::f() - это то же самое, что и inline Derived::f()?

Я имею в виду, что ключевое слово inline неявно указано для Derived::f(), или мне нужно явно ввести его еще раз?

Ответы [ 2 ]

3 голосов
/ 27 марта 2019

Нужно ли указывать inline в Derived::f() или я могу опустить ключевое слово и быть уверенным, что virtual Derived::f() - это то же самое, что и inline Derived::f()?

Есливы опускаете ключевое слово inline в производном классе, в производном классе оно не inline.

Я имею в виду ключевое слово inline, неявно указанное для Derived::f()

Нет, это не так.

или мне нужно явно набрать его еще раз?

Да, вы делаете.Тем не менее, компилятор, скорее всего, сгенерирует для него код, как если бы он не был inline функцией-членом, поскольку он является virtual функцией-членом.

1 голос
/ 27 марта 2019

Что делает встроенное ключевое слово?

Современные компиляторы пытаются сбалансировать затраты на вставку функции с преимуществами.

Как преимущества, так и затраты довольно очевидны: когда функция встроена, нет необходимости в вызове функции (так как нет вызова функции), и компилятор может оптимизировать тело функции на основе в контексте, в котором он вызывается (поскольку, когда он встроен, он знает этот контекст).

Затраты могут включать в себя увеличение размера исполняемого файла (если это большая функция) и увеличение количества экземпляров сборки для тела функции, наложенной вокруг исполняемого файла.

Практическое правило: если функция большая или сложная, она, вероятно, не будет встроенной. Если он маленький, он, вероятно, будет встроен.

Это хорошо. Это предотвращает раздутые исполняемые файлы, но все же устраняет почти все накладные расходы, связанные с использованием функций Время выполнения большой, сложной функции часто карлики стоимость вызова функции, и ее включение будет иметь лишь минимальную выгоду.

Так что же делает inline? 1017 * Компилятор вычисляет, насколько сложна функция, когда решает встроить ее. Затем он сравнивает этот расчет с некоторым порогом. Если функция менее сложна, чем пороговое значение, она указывает на функцию.

Ключевое слово inline в основном поднимает порог для этой конкретной функции, но то, что она на самом деле делает под капотом, варьируется от компилятора к компилятору.

Можно ли встраивать все вызовы функций?

Если компилятор не знает, какая функция вызывается, он не может встроить ее.

Давайте рассмотрим пример:

// func_t is a pointer to a function that returns an integer
using func_t = int(*)(); 
int getNumber(func_t func) {
    // The compiler can't inline func(), because it doesn't know
    // what func *is*
    return func(); 
}

Как это относится к виртуальным функциям?

Виртуальный вызов функции очень похож на вызов указателя функции, но с некоторыми ключевыми отличиями. Если вы вызываете его из базового класса, компилятор не знает заранее, какую функцию вызывать:

class Base {
    virtual int getNum() { return 0; }
};

class Derived {
    int value; 
    void setValue(int v) { value = v; }
    int getNum() override { return value; }
}; 

int getNumFrom(Base& base) {
    // The compiler doesn't know whether to call
    // Base::getNum() or Derived::getNum(), so it can't inline it
    return base.getNum();
}

Однако, если вы вызываете его из конкретного экземпляра класса (не ссылки и не указателя, компилятор точно знает, какая версия вызывается:

int getNumFromDerived() {
    Derived d; 
    // Here, we know that we're calling Derived::getNum()
    // so the compiler *can* inline it. 
    return d.getNum(); 
}

Как применять встроенное ключевое слово?

Вы можете указать его как в базовом классе, так и в производном классе. Просто знайте, что не будет гарантировать, что они встроены, именно потому, что иногда невозможно встроить вызов виртуальной функции.

Есть ли альтернативы?

Поскольку шаблоны сохраняют информацию о типе, компилятор всегда знает, какую функцию вызывать. Легко встроить вызовы шаблонных функций, и их использование не приведет к дополнительным расходам в вашей программе.

Если возможно, предпочтите шаблоны виртуальному наследованию.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...