Информация об изменениях

Сообщение Re[4]: constexpr + mutable от 10.09.2023 11:34

Изменено 10.09.2023 11:43 Sm0ke

Re[4]: constexpr + mutable
Здравствуйте, YuriV, Вы писали:

S>>В рантайм Меняется состояние constexpr константы.

YV>constexpr говорит лишь о том, что компилятор сначала может попытаться вычислить значение выражение в компайл-тайм и если по каким-то причинам не может, то забывает о constexpr, неважно что это объявление метода или объекта. Выражение становиться просто константным, а мьютабл вы сами написали в классе. Так что ничего занятного.

constexpr у метода прдоставляет возможность вызвать этот метод не только в run-time, но и в compile-time.
Однако эту возможность constexpr не гарантирует.
Компилятор решает исходя из контекста вызова — является ли этот вызов compile-time вычислением, или run-time вычислением.

  например
enum {
n_value = some_funct() // compile-time call
};


В случае compile-time происходит проверка допустимости выражения (и в вызове может быть отказано).

В коде моего исходного поста ...

#include <iostream>

struct t_const_mut
{
  // data
  mutable int
  value;

  constexpr int
  get() const
  { return ++this->value; }
};

constexpr const t_const_mut // и constexpr и const
g_obj{0}; // глобальная ячейка

int main()
{
  std::cout
  << g_obj.get() << ' '
  << g_obj.get() << '\n';
  
  return 0;
}


... метод get() const меняет mutable свойство того объекта, который лежит за пределами body этого метода.
И компилятор запрещает вызов этого метода в compile-time, несмотря на то что метод constexpr.
пруф: https://godbolt.org/z/vqvrf95PM (другой пример)
  clang
<source>:18:5: error: non-type template argument is not a constant expression
TAG<g_obj.get()> g_tag;
^~~~~~~~~~~
<source>:11:12: note: a constant expression cannot modify an object that is visible outside that expression
{ return ++this->value; }
^
<source>:18:11: note: in call to '&g_obj->get()'
TAG<g_obj.get()> g_tag;
^
1 error generated.
Compiler returned: 1


Но даже если убрать инкремент, компилятор всё равно запретит (из-за чтения mutable у внешнего объекта).
пруф: https://godbolt.org/z/P3aq8n613 (ещё другой пример)
  clang
<source>:18:5: error: non-type template argument is not a constant expression
TAG<g_obj.get()> g_tag;
^~~~~~~~~~~
<source>:11:12: note: read of mutable member 'value' is not allowed in a constant expression
{ return this->value; }
^
<source>:18:11: note: in call to '&g_obj->get()'
TAG<g_obj.get()> g_tag;
^
<source>:7:3: note: declared here
value;
^
1 error generated.
Compiler returned: 1


А что если обращаться в методе get() к mutable свойству у локального объекта?
проверим: https://godbolt.org/z/nq3TT4zbr (пример с локальным объектом метода get)

ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
1 1


Компилируется!

#include <iostream>

struct t_const_mut
{
  // data
  mutable int
  value;

  constexpr int
  get() const
  { t_const_mut v{}; return ++v.value; }
};

constexpr const t_const_mut
g_obj{0};

template <int I> struct TAG {};
TAG<g_obj.get()> g_tag;

int main()
{
  std::cout
  << g_obj.get() << ' '
  << g_obj.get() << '\n';
  
  return 0;
}
Re[4]: constexpr + mutable
Здравствуйте, YuriV, Вы писали:

S>>В рантайм Меняется состояние constexpr константы.

YV>constexpr говорит лишь о том, что компилятор сначала может попытаться вычислить значение выражение в компайл-тайм и если по каким-то причинам не может, то забывает о constexpr, неважно что это объявление метода или объекта. Выражение становиться просто константным, а мьютабл вы сами написали в классе. Так что ничего занятного.

constexpr у метода прдоставляет возможность вызвать этот метод не только в run-time, но и в compile-time.
Однако эту возможность constexpr не гарантирует.
Компилятор решает исходя из контекста вызова — является ли этот вызов compile-time вычислением, или run-time вычислением.

  например
enum {
n_value = some_funct() // compile-time call
};


В случае compile-time происходит проверка допустимости выражения (и в вызове может быть отказано).

В коде моего исходного поста ...

#include <iostream>

struct t_const_mut
{
  // data
  mutable int
  value;

  constexpr int
  get() const
  { return ++this->value; }
};

constexpr const t_const_mut // и constexpr и const
g_obj{0}; // глобальная ячейка

int main()
{
  std::cout
  << g_obj.get() << ' '
  << g_obj.get() << '\n';
  
  return 0;
}


... метод get() const меняет mutable свойство того объекта, который лежит за пределами body этого метода.
И компилятор запрещает вызов этого метода в compile-time, несмотря на то что метод является constexpr.
пруф: https://godbolt.org/z/vqvrf95PM (другой пример)
  clang
<source>:18:5: error: non-type template argument is not a constant expression
TAG<g_obj.get()> g_tag;
^~~~~~~~~~~
<source>:11:12: note: a constant expression cannot modify an object that is visible outside that expression
{ return ++this->value; }
^
<source>:18:11: note: in call to '&g_obj->get()'
TAG<g_obj.get()> g_tag;
^
1 error generated.
Compiler returned: 1


Но даже если убрать инкремент, компилятор всё равно запретит (из-за чтения mutable у внешнего объекта).
пруф: https://godbolt.org/z/P3aq8n613 (ещё другой пример)
  clang
<source>:18:5: error: non-type template argument is not a constant expression
TAG<g_obj.get()> g_tag;
^~~~~~~~~~~
<source>:11:12: note: read of mutable member 'value' is not allowed in a constant expression
{ return this->value; }
^
<source>:18:11: note: in call to '&g_obj->get()'
TAG<g_obj.get()> g_tag;
^
<source>:7:3: note: declared here
value;
^
1 error generated.
Compiler returned: 1


А что если обращаться в методе get() к mutable свойству у локального объекта?
проверим: https://godbolt.org/z/nq3TT4zbr (пример с локальным объектом метода get)

ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
1 1


Компилируется!

#include <iostream>

struct t_const_mut
{
  // data
  mutable int
  value;

  constexpr int
  get() const
  { t_const_mut v{}; return ++v.value; }
};

constexpr const t_const_mut
g_obj{0};

template <int I> struct TAG {};
TAG<g_obj.get()> g_tag;

int main()
{
  std::cout
  << g_obj.get() << ' '
  << g_obj.get() << '\n';
  
  return 0;
}