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

от автора

Просто хотел Вас предупредить: С++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; } }; 

ссылка на оригинал статьи http://habrahabr.ru/post/184250/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *