Если я разделю код на файлы .h и .cpp, какие из следующих ключевых слов, используемых в заголовке, также должны использоваться в файле .cpp, а какие не должны входить в файл .cpp:
const, virtual, override, noexcept, constexpr
и в каком порядке их следует использовать, т. е. правильный ли следующий порядок:
virtual constexpr int foo() const override noexcept;
Пожалуйста, не стесняйтесь указать правило, если оно существует.
Не имеет значения, в каком файле они находятся; «файл» — в значительной степени бессмысленное понятие в C++. Разница заключается в определениях членов внутри и вне определения класса и описана в любой приличной книге.
Вы хотели спросить, должно ли ключевое слово, использованное в объявлении функции или переменной, повторяться в ее определении, когда определение предоставляется после объявления?
@tbxfreeware да, именно это я и имел в виду.
Существует также разница между «должно повторяться» и «можно повторять». Некоторые из нас предпочитают иметь virtual
на всех виртуальных функциях. даже когда это не нужно.
override
— это спецификатор, то есть это не ключевое слово, а идентификатор, который имеет особое значение при использовании в определенном контексте. (Другой спецификатор в C++ — final
. В будущем их может быть больше.) Почему не ключевое слово? WG21 очень консервативна в добавлении новых ключевых слов в язык и обычно вместо этого переназначает существующее ключевое слово (именно поэтому некоторые ключевые слова, такие как inline
, теперь имеют совсем другую семантику, чем та, что была изначально, или static
имеет десятки разных значений в разных контекстах).
const и noException необходимо будет продублировать в исходном коде, поскольку они являются частью сигнатуры функции.
«Виртуальные» спецификаторы являются частью определения класса и относятся только к спецификации члена класса, которая в данном случае является объявлением (с определением или без него) нестатической функции-члена класса. Член, являющийся «виртуальным», является свойством класса, а не типа функции.
«Квалификаторы» (const, voluty, &
, &&
) и спецификаторы constexpr
/consteval
являются частью функции и, следовательно, должны присутствовать в каждом объявлении функции (члена) (включая определяющее, независимо от того, является ли оно встроенным или из линии):
// Class definition of X; specifies the members of X.
struct X : Base {
virtual void f() const; // virtual and override
void g() && override; // are part of "being a member"
};
// Function declarations and definitions:
// qualifiers are part of the function.
void X::f() const { /* ... */ }
void X::g() && { /* ... */ }
Это не имеет ничего общего с файлами .h и .cpp как таковыми, хотя обычно в файл .cpp помещают внеочередное определение. (Если бы его оставили в заголовке, это привело бы к повторным определениям, когда заголовок включен в разные единицы перевода, если только одно из объявлений также не было явно указано как inline
. Напротив, встроенные определения функций-членов класса неявно являются inline
. )
Хороший ответ, и спасибо, что указали на реф-квалификаторы. ТИЛЬ!
Этот список весьма неполный. noexcept
-спецификации, языковая связь, ограничения и т. д. также должны совпадать между объявлением и определением. Или, я думаю, вообще все, что является частью сигнатуры функции.
@JanSchultke Да, правда - именно поэтому я пытался указать на основное различие между «членом класса» и «типом функции». Детали наверняка изменятся по мере развития C++. Спасибо за дополнения.
Короче говоря, вам нужно повторить части функции, которые являются частью ее типа и/или необходимы для соответствия двух функций. Поэтому нужно повторить:
const
и volatile
&
и &&
)requires
пунктыnoexcept
характеристикиКак правило, чтобы объявление функции в заголовке соответствовало определению в исходном файле, объявления должны соответствовать. Для функций это означает, что они должны быть соответствующими перегрузками (см. [basic.scope.scope]):
Два объявления функции или шаблона функции объявляют соответствующие перегрузки, если:
- обе объявляют функции с одинаковым списком типов параметров, не являющихся объектами , эквивалентными ([temp.over.link]) завершающими предложениями require (если таковые имеются, за исключением случаев, указанных в [temp.friend]), и, если оба являются нестатическими членами, они имеют соответствующие параметры объекта, или
- оба объявляют шаблоны функций с соответствующими сигнатурами и эквивалентными заголовками шаблонов и завершающими предложениями require (если таковые имеются).
Давайте сосредоточимся только на первом пункте, поскольку вы не спрашиваете о шаблонах.
Во-первых, список параметров должен быть одинаковым (не считая явного параметра объекта, т. е. this auto
и других форм).
Во-вторых, параметр объекта должен быть одинаковым, а это означает, что const
, volatile
, &
и &&
должны совпадать.
В-третьих, предложение requires
должно быть эквивалентным.
Спецификации noexcept
также должны быть одинаковыми, поскольку noexcept
относится к типу функции. Вы не можете иметь два соответствующих объявления разных типов:
int x(int);
int x(float); // OK, x(float) does not correspond to x(int) and is a separate overload
int x(int) noexcept; // error: x(int) declared with two different types
Все остальное, например атрибуты, virtual
и т. д., применимо к объявлению и не должно повторяться в определении.
constexpression
не является ключевым словом — вы, вероятно, имеете в видуconstexpr
, аoverride
не является ключевым словом. В любом случае (1) по соглашению файлы .cpp#include
файлы заголовков и файлы заголовков могут (необязательно)#include
друг друга и (2) эффект#include
заключается в выполнении замены текста и замене содержимого директивы#include
содержимым. названного файла. Следовательно, любая конструкция (включая любое ключевое слово), которая появляется в заголовке, может появиться в исходном файле, и наоборот.