Как стать автором
Обновить

«constexpr» функции не имеют спецификатор «const»

Время на прочтение 3 мин
Количество просмотров 15K
Автор оригинала: Andrzej Krzemieński
Просто хотел Вас предупредить: С++14 не будет обратно совместим с C++11 в одном аспекте constexpr функций.

В С++11, если Вы определите constexpr функцию-член, то она неявно получит спецификатор const:
// C++11
struct NonNegative
{
  int i;
  constexpr int const& get() /*const*/ { return i; }
  int& get() { return i; }
};

Первое объявление функции get получит спецификатор const, даже если мы не укажем это явно. Следовательно, эти две функции являются перегруженными: const и не-const версии.

В С++14 это будет уже не так: оба объявления будут определять одиннаковую, не-const версию функции-члена с различающимися возвращаемыми значениями — это приведет к ошибке компиляции. Если Вы уже начали использовать constexpr функции и надеетесь на неявный спецификатор const, то я советую Вам начать добавлять его явно, чтобы Ваш код продолжал компилироваться, если Вы решите перейти на компиляторы С++14.

Что не так с неявным const?


Проблемы начнутся, если Вы попытаетесь использовать наш тип следующим образом:
// C++11
constexpr int i = NonNegative{2}.get(); // ERROR

Согласно (несколько необычным) правилам С++, при выборе функции-члена для временного объекта, не-const версия предпочтительней const версии. Наша не-const функция-член get не является constexpr, поэтому она не может быть использована для инициализации constexpr переменной и мы получим ошибку на этапе компиляции. Мы не можем сделать эту функцию constexpr, потому что это автоматически добавит спецификатор const

Я сказал, что правила подбора лучшей функции — необычны, потому что они немного противоречат тому, как мы выбираем лучшую функцию-нечлен для временных объектов. В этом случае мы предпочитаем const lvalue ссылки к не-const версии:
// C++11
constexpr int const& get(NonNegative const& n) { return n.i; }
constexpr int& get(NonNegative& n) { return n.i; }
 
NonNegative N = readValue();
constexpr int * P = &get(N);
 
int main() 
{ 
  *P = 1;
}

Смотрите, что получается: глобальная переменная N не является константой. Поэтому вторая, не-const перегруженная функция выбирается для вызова при инициализации указателя P. Но не-const функция при этом все равно имеет спецификатор constexpr! А все потому, что правило "constexpr означает const" применяется только для неявного this аргумента нестатической функции-члена. constexpr функция может получить ссылку на не-const объект и вернуть ссылку на не-const подобъект. Здесь нету никаких проблем: адрес глобального объекта постоянен и известен во время компиляции. Однако значение по адресу P не постоянно и может быть изменено позже.

Если предыдущий пример выглядит несколько надуманным, рассмотрим следующий, более реалистичный пример:
// C++11
constexpr NonNegative* address(NonNegative& n) { return &n; } 
 
NonNegative n{0}; // non-const
constexpr NonNegative* p = address(n);

Здесь все работает отлично, но если Вы попытаетесь сделать address функцией членом, она перестанет работать:
// C++11
struct NonNegative
{
  // ...
  constexpr NonNegative* maddress() { return this; } // ERROR
};
 
NonNegative n{0}; // non-const
constexpr NonNegative* p = n.maddress();

Это потому, что maddress неявно определен со спецификатором const, this имеет тип NonNegative const* и не может быть конвертировано к NonNegative*.

Следует отметить, что это не сама функция-член является const, а неявный (this) аргумент функции. Объявление функции-члена может быть переписано в псевдо-коде как:
// PSEUDO CODE
struct NonNegative
{
  // ...
  constexpr NonNegative* maddress(NonNegative const& (*this)); 
};

И этот неявный аргумент функции, в отличие от других аргументов функций, получает (иногда нежелательный) спецификатор const.

Эта асимметричность будет удалена в С++14. Если Вы хотите спецификатор const для неявного аргумента (this), Вам следует добавить его самим. Следующий код будет действительным в C++14:
// C++14
struct NonNegative
{
  int i;
  constexpr int const& get() const { return i; }
  constexpr int& get() { return i; }
};
Теги:
Хабы:
+31
Комментарии 26
Комментарии Комментарии 26

Публикации

Истории

Работа

Программист C++
121 вакансия
QT разработчик
13 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн