Подтвердить что ты не робот

Встроенная функция члена С++ в файле .cpp

Я знаю, что встроенные функции-члены по определению должны войти в заголовок. Но что, если невозможно включить реализацию функции в заголовок? Возьмем эту ситуацию:

Файл A.h

#pragma once
#include "B.h"

class A{
    B b;
};

Файл B.h

#pragma once

class A; //forward declaration

class B{
    inline A getA();
};

Из-за кругового включения я должен поставить реализацию getA в

B.cpp

#include "B.h"
#include "A.h"

inline A B::getA(){
    return A();
}

Будет ли компилятор inline getA? Если это так, какое ключевое слово inline является значительным (в заголовке или в файле .cpp)? Есть ли другой способ поместить определение встроенной функции-члена в его .cpp файл?

4b9b3361

Ответ 1

Цитата из Часто задаваемые вопросы по С++:

Примечание. Необходимо, чтобы определение функции (часть между {...}) помещается в заголовочный файл, если только функция не является используется только в одном файле .cpp. В в частности, если вы установите встроенный определение функции в .cpp файл и вы вызываете его из другого .cpp файла, вы получите "неразрешенный внешняя" ошибка от компоновщика.

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

Будет ли компилятор inline getA?

Нет, кроме случаев, когда использование getA() принадлежит самому B.cpp.

Если это так, какое ключевое слово inline является значительным (то есть в заголовке или в одном из cpp)?

Лучшая практика: только в определении вне тела класса.

Есть ли другой способ поместить определение встроенной функции-члена в файл cpp?

Нет, по крайней мере, я не знаю.

Ответ 2

Он не может, вне сферы действия B.cpp. Компилятор работает с базой для каждого компилятора, т.е. Он компилирует каждый файл .cpp отдельно, поэтому, если он компилирует C.cpp, он не будет иметь код для getA() и ему потребуется выполнить вызов функции и (или, если это действительно взяло вас под словом и попыталось встроить, оно закончится ошибкой компоновщика. inline имеет похожие качества, такие как static).

Единственным исключением является LTCG, то есть генерация кода link-time, которая доступна для более новых компиляторов.

Одним из подходов в этом случае является наличие другого файла заголовка (иногда называемого *.inl файлами), который содержит встроенный код.

РЕДАКТИРОВАНИЕ. Что касается того, какая строка является актуальной, - это то, что указано в определении класса, то есть в файле заголовка. Имейте в виду, что многие компиляторы имеют свой собственный подход к тому, что может и должно быть встроено. Например, gcc может полностью отключить вложение (-O0), или он может встроить все, что он считает целесообразным, встраивание (например, -O3).

Ответ 3

Я бы сделал это в противоположном направлении.

Не добавляйте встроенные объявления к вашей функции (если вам тоже не нужно).

Единственный раз, когда вам нужно добавить объявление inline в функцию/метод, является определение функции в файле заголовка, но вне объявления класса.

X.h

class X
{
    public:
        int getX()   { return 4;} // No inline because it is part of the class.
                                  // The compiler knows that needs an inline tag
        int getY();
        int getZ();
};

inline
int X::getY()  { return 5;}       // This needs to be explicitly declared inline.
                                  // Otherwise the linker will complain about
                                  // multiple definitions in compilation units.

x.cpp

 // Never declare anything inline in the cpp file.

 int X::getZ() { return 6; }

Вам более конкретный случай.
Удалите все встроенные спецификации. Они не делают то, что, по вашему мнению, делают.

Ответ 4

В наши дни большинство компиляторов могут выполнять inlining во время соединения, а также время компиляции. Если ваша функция, скорее всего, выиграет от вложения, тогда оптимизатор времени связи, скорее всего, сделает именно это.

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

Ответ 5

Вот как я это сделал.

Файл A.h

#pragma once
#include "B.h"

class A {
    B b;
};

Файл B.h

#pragma once

class B {
public:
    template<class T> inline T getA() {
        assert(NULL); // Use 'getA<A>()' template specialization only!
        return NULL;
    }
};

class A; // Forward declaration
template<> inline A B::getA<A>();

Файл C.h

#pragma once
#include "A.h"
#include "B.h"

// Implement template specialization here!
template<> inline A B::getA<A>() { return A(); }

Просто включите файл C.h, чтобы использовать метод getA(). Единственное изменение с исходным кодом заключается в том, что метод getA() должен быть определен как public, а не private.

Однако, как многие из вас объяснили это, это не очень полезно.