У меня следующая проблема. У меня есть эти 3 файла (я сделал упрощенный пример, но ошибки те же):
foo.hpp
#pragma once
#include <iostream>
class foo
{
protected:
virtual void bar() const noexcept = 0;
public:
foo() = default;
virtual void callbar() = 0;
};
class baz : public foo
{
protected:
void bar() const noexcept override;
public:
void callbar();
};
foo.cpp
#include "foo.hpp"
inline void baz::bar() const noexcept { std::cout << "baz::bar()" << '\n'; }
inline void baz::callbar() { bar(); }
main.cpp
#include "foo.hpp"
auto main() -> int
{
baz b;
b.callbar();
}
Компилятор (на самом деле компоновщик, я думаю) дает мне следующую ошибку:
foo.cpp
main.cpp
Generating Code...
Microsoft (R) Incremental Linker Version 14.15.26729.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:foo.exe
foo.obj
main.obj
main.obj : error LNK2001: unresolved external symbol "protected: virtual void __cdecl baz::bar(void)const " (?bar@baz@@MEBAXXZ)
main.obj : error LNK2019: unresolved external symbol "public: virtual void __cdecl baz::callbar(void)" (?callbar@baz@@UEAAXXZ) referenced in function main
foo.exe : fatal error LNK1120: 2 unresolved externals
Теперь я обошел это, выполнив одну из этих двух вещей:
inline
inline
как есть, но переместите определения методов в файл .hpp
Если я делаю одну из этих вещей, все работает. Но мой вопрос: почему? В моем реальном коде я действительно хочу, чтобы компилятор встраивал вызовы методов, плюс я хочу, чтобы они были определены в файле .cpp
, чтобы сделать мой файл .hpp
более понятным. Есть ли решение для этого?
Ну, я знаю, что если метод определен полностью в теле класса, то он встроен по умолчанию, но если я определяю его снаружи, то это не так, и я хочу, чтобы это было так. Потому что эти методы будут вызываться очень часто, и я не хочу терять производительность из-за накладных расходов на вызов функций.
Как вы думаете, что вы получаете, используя inline
? Удаление inline
— правильное решение.
@dabljues Вы неправильно понимаете, что делает inline
. Удивительно, но это не имеет ничего общего с встраиванием функций.
Гоша, это неловко. Теперь буду знать, спасибо! Должен ли я тогда удалить/закрыть вопрос?
Возможный дубликат Встроенная функция-член С++ в файле .cpp
В вашем коде есть ошибка. Согласно cppreference
The definition of an inline function or variable (since C++17) must be present in the translation unit where it is accessed (not necessarily before the point of access).
Очевидно, что когда вы помещаете свои определения в файл .cpp и вызываете эти функции из других единиц перевода, это условие не выполняется.
Таким образом, ваши два альтернативных подхода работают, потому что они либо
И последнее, но не менее важное: спецификатор C++ inline
не имеет ничего общего с встраиванием функций. Существуют и другие, зависящие от компилятора способы запроса фактического встраивания, то есть __forceinline
в различных компиляторах.
Спасибо! Ответ этого парня, который @Aconcagua вставил: stackoverflow.com/a/5971755/1312382, предполагает: «Конечно, это в дополнение к подсказке компилятору о том, что функция должна быть встроена в то место, где она используется (избегая накладных расходов на вызов функции)». Так он ошибается тогда?
@dabljues да, это уже давно устарело. Компиляторы в настоящее время полностью игнорируют inline
, когда дело доходит до встраивания функций, и делают это на основе своих собственных эвристических алгоритмов. Более того, встраивание все чаще становится свойством сайта вызова, а не самой функции — функция может быть вставлена в одном месте, а вызвана в другом.
Спасибо, это действительно прояснило!
Компилятор @SergeyA на самом деле все еще использует объявление inline
в своей эвристике. И функция, являющаяся встроенной, во многом связана с встроенной функцией, потому что компилятор не может встраивать вызовы функций через TU. (хотя, это возможно для линкеров).
@eerorika Я по-прежнему утверждаю, что inline
является свойством языка, а не фактическим встраиванием. Например, функция, которая не объявлена встроенной, все равно будет встроенной, если ее определение видимо, при условии, что она является встроенной. Верно и обратное — встроенная функция, которая не является встроенной, не будет встроенной. Итог - спецификатор не связан.
@SergeyA Со всем согласен, кроме твоего вывода. Прямоугольник может быть очень широким, но иметь маленькую площадь (если у него достаточно маленькая высота). Прямоугольник может быть очень узким, но иметь большую площадь (если он достаточно высокий). Таким образом, вы можете заключить, что площадь прямоугольника не связана с шириной прямоугольника. Но это был бы ложный вывод: они напрямую связаны. В вашем выводе та же проблема.
@eeorika, мне кажется, что волосы лезут в лоб. С практической точки зрения, я думаю, что мы здесь действительно согласны.
Почему
inline
в определениях?