Вбудована функція члена C ++ у файлі .cpp


76

Я знаю, що вбудовані функції члена за визначенням повинні йти в заголовок. Але що, якщо неможливо помістити реалізацію функції в заголовок? Візьмемо таку ситуацію:

Файл Ах

#pragma once
#include "B.h"

class A{
    B b;
};

Файл Bh

#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();
}

Чи буде компілятор вбудованим getA? Якщо так, яке вбудоване ключове слово є значущим (ключовим у заголовку чи ключовим файлом .cpp)? Чи існує інший спосіб внести визначення вбудованої функції-члена у файл .cpp?


Чи маєте ви помилкове враження і думаєте, що ключове слово inline означає, що компілятор генерує вбудовану збірку, замість того, щоб робити виклик функції?
Мартін Йорк,

Нах - я знаю , що має статися
Mat

Ти впевнений. Що, на вашу думку, має статися?
Мартін Йорк,

3
@Mat. Ні. Це нічого не означає з точки зору вбудованого коду. Це лише підказка (яку всі сучасні компілятори ігнорували). Немає вимоги до компілятора що-небудь робити. Компілятор аналізує всі функції для потенційного вбудовування, чи використовується ключове слово "вбудований".
Martin York

3
так, я знаю, що це лише підказка - у будь-якому випадку ви кажете, що ключове слово "вбудований" застаріло?
Mat

Відповіді:


74

Посилання на поширені запитання на C ++ :

Примітка: Обов’язково потрібно, щоб визначення функції (частина між {...}) було розміщено у файлі заголовка, якщо функція не використовується лише в одному файлі .cpp. Зокрема, якщо ви вкладете визначення вбудованої функції у файл .cpp і викликаєте його з іншого файлу .cpp, ви отримаєте помилку "невирішена зовнішня" від компонувальника.

Компілятор повинен бачити визначення вбудованої функції, коли він знаходить якесь використання цієї вбудованої функції. Як правило, це можливо, якщо вбудована функція розміщується у файлі заголовка.

Чи вбудований компілятор отримає A?

Ні, за винятком випадків, коли використання використовується getA()в самому B.cpp.

Якщо так, яке вбудоване ключове слово є значущим (ключовим у заголовку чи ключовим словом cpp)?

Краща практика : лише у визначенні поза тілом класу.

Чи є інший спосіб помістити визначення вбудованої функції-члена у файл cpp?

Ні, принаймні я не знаю.


21

Це не може, поза сферою дії B.cpp. Компілятор працює на основі одиниці компіляції, тобто він компілює кожен файл .cpp окремо, тому, якщо він компілює C.cpp, у нього не буде коду getA (), і йому потрібно буде виконати виклик функції та попросіть компонувальник виправити це (або, якщо він насправді взяв вас за слово і спробував вбудувати, він в кінцевому підсумку отримає помилку лінкера. inlineмає подібні якості, як static).

Єдиним винятком є ​​LTCG, тобто генерація коду часу зв'язку, яка доступна на новіших компіляторах.

Одним із підходів у цьому випадку є наявність іншого заголовного файлу (іноді з назвою * .inl-файли), що містить вбудований код.

РЕДАГУВАТИ: Що стосується того, який рядок є важливим - це той, що є у визначенні класу, тобто у файлі заголовка. Майте на увазі, що багато компілятори мають власну думку щодо того, що можна і що слід вкладати. gcc, наприклад, може повністю відключити вставку (-O0), або може вбудувати все, що вважає вартим вкладання (наприклад, -O3).


тож - у моєму прикладі - я б просто перейменував B.cpp на B.inl і все, де я зазвичай включав би Bh, а замість цього я б включив B.inl?
Mat

1
Я б не перейменовував B.cpp у B.inl - я б створив окремий B.inl (або бажано B_inline.h) і помістив би туди всі вбудовані функції. Потім у своєму B.cpp включіть файл _inline.h.
EboMike

1
"Це не може, поза сферою дії B.cpp ..." - Деякі з причин NO можуть змінитися зараз, коли деякі ланцюжки інструментів мають оптимізацію часу зв'язку (LTO). Це можна ввімкнути -fltoпід час використання GCC та деяких інших компіляторів.
jww

@jww Не впевнений, чи читали ви до другого абзацу, але саме тут я згадав LTCG / LTO :) Що, звичайно, зараз частіше, ніж у 2010 році, коли я писав це. Хоча особисто мені все-таки подобається писати код на C ++, який допомагає компілятору оптимізувати, додаючи вбудовані функції у файли заголовків.
EboMike

8

Я б пішов про це з протилежного напрямку.

Не додавайте вбудовані декларації до вашої функції (якщо вам це теж не потрібно).

Єдиний раз, коли вам потрібно додати вбудовану декларацію до функції / методу, це якщо ви визначаєте функцію у файлі заголовка, але поза оголошенням класу.

Xh

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; }

Для вас більш конкретний випадок.
Видаліть усі вбудовані технічні характеристики. Вони роблять не те, що, на вашу думку, вони роблять.


1
ах - отже, якщо функція визначена в тілі класу, вона все одно буде вбудована?
Mat

1
@Mat: Він буде позначений так, ніби було застосовано вбудоване ключове слово. Не плутайте це з вбудованим кодом компілятора. Усі сучасні компілятори повністю ігнорують тег (для педантичного: компонувальник використовує його (але не для вбудовування)).
Мартін Йорк,

7

У наші дні більшість компіляторів можуть виконувати вбудовування під час зв’язування, а також під час компіляції. Якщо ваша функція, швидше за все, виграє від вбудовування, тоді оптимізатор часу зв’язку, ймовірно, зробить саме це.

До того часу, як компонувальник дійде до нього, доступне не так багато про вбудований стан виводу компілятора, за винятком того, що компілятор буде позначати певні об'єкти колекційними, наприклад, тому що вбудована функція або екземпляр шаблону класу з'являється в декількох одиницях компіляції, це повинно викликати помилку, коли декілька символів мають спільне ім'я, наприклад, головна функція визначається двічі. Ніщо з цього не впливає на фактичний код, який він створить.


Я не знав, що це можливо. лінкер дбає про ключове слово "вбудований" або він просто сам вирішує, що вбудувати?
Mat

2
Я не був би настільки впевнений у „самій” частині. Крім того, LTCG працює жахливо, і мені особисто подобається більше контролювати те, що робить компілятор.
EboMike

1
@EboMike: Контроль, який ти вважаєш своїм, - це ілюзія. Компілятор завжди буде кращий за вас, вирішуючи, що варто вкласти. (Люди жахливо роблять необхідний аналіз). Нехай компілятор виконує роботу, яку він мав робити (вбудовуючи), нехай людина робить те, що їй вдається (думаючи про алгоритми).
Martin York,

1
@Mat: Усі сучасні компілятори ігнорують вбудоване ключове слово (стосовно вбудованого коду) (якщо ви не примушуєте їх теж прапорцем компілятора, і це завжди помилка (якщо ви насправді не знаєте, що робите, і якщо думаєте, що робите) тоді ви цього не робите)). Посилання дійсно потребує вбудованого ключового слова, щоб знати, що він повинен ігнорувати декілька визначень (або консолідувати кілька визначень функції), інакше він повинен припустити, що ви зіпсували і створили помилку.
Martin York,

0

Ось як я це зробив.

Файл Ах

#pragma once
#include "B.h"

class A {
    B b;
};

Файл Bh

#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>();

Файл Ch

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

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

Просто включіть файл "Ch", щоб мати можливість використовувати метод getA (). Єдиною зміною оригінального коду є те, що метод getA () повинен визначатися як загальнодоступний, а не як приватний.

Однак, як багато хто з вас пояснювали це, це насправді не корисно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.