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

Можно ли объявить класс constexpr в заголовке и определить его в отдельном файле .cpp?

У меня есть класс Dimension, который я определил (как и все мои классы) в файле Dimension.h:

class Dimension
{
public:

    constexpr Dimension() noexcept;

    constexpr Dimension(int w, int h) noexcept;

    int width;
    int height;

};

Я думал, что мог, как и во всех моих классах, поставить определение в отдельный Dimension.cpp:

#include "Dimension.h"

constexpr Dimension::Dimension() noexcept : width(0), height(0) {}

constexpr Dimension::Dimension(int w, int h) noexcept : width(w), height(h) {}

Но когда я пытаюсь использовать класс, компилятор говорит мне:

предупреждение: встроенная функция 'constexpr Dimension::Dimension()' используется, но не определена

и при связывании:

undefined ссылка на 'pong::graphics::Dimension::Dimension()'

(то же самое с другим конструктором)

Если я определяю класс в заголовке так:

class Dimension
{
public:

    constexpr Dimension() noexcept : width(0), height(0) {}

    constexpr Dimension(int w, int h) noexcept : width(w), height(h) {}

    int width;
    int height;

};

и опустить файл .cpp, все работает нормально.

Я использую GCC 4.9.2. Почему отдельное определение не работает?

4b9b3361

Ответ 1

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

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

Спасибо @IgorTandetnik:
[dcl.constexpr] §7.1.5/2

constexpr Функции и конструкторы constexpr неявно встроены.

[basic.def.odr] §3.2/4

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

Ответ 2

То, что вы запрашиваете, может быть выполнено со значительным ограничением: функция constexpr может быть вызвана только изнутри единицы перевода (то есть исходного файла), где она определена. Это не подходит для примера, который вы дали, поскольку конструктор должен быть частью открытого интерфейса класса. Однако это может быть очень полезно в других случаях, например. для определения частных методов как constexpr, а затем использовать их возвращаемые значения в выражениях, которые должны быть известны во время компиляции, например. шаблонные экземпляры, метки оператора switch и т.д.