LINUX.ORG.RU

constexpr в C++ на самом деле не const

 , ,


2

4

Привет, ЛОР!

Нашёл забавную фишку про C++. Если вкратце, можно сделать, чтобы следующий кусок кода не вываливался с ошибкой при сборке.

int main () {
  constexpr int a = f ();
  constexpr int b = f ();

  static_assert (a != b, "fail");
}

Как это сделать? Об этом написано тут: https://b.atch.se/posts/non-constant-constant-expressions/

Если вкратце, то C++ стал настолько монструозен, что разные части стандарта могут прямо друг другу противоречить, и вместе эти фичи языка дают прямо таки неожиданные результаты. В итоге, можно сделать так, чтобы функция, помеченная как constexpr, на самом деле в каждом вызове выдавала рандомное значение в зависимости от фазы луны. Если очень хочется.

P.S. первый пример из ссылки GCC сейчас обрабатывает корректно и вываливает ошибку из static_assert. Но второй ещё работает в GCC 13. Для Ъ код ниже.

namespace detail {
  struct A {
    constexpr A () { }
    friend constexpr int adl_flag (A);
  };

  template<class Tag>
  struct writer {
    friend constexpr int adl_flag (Tag) {
      return 0;
    }
  };
}

template<class Tag, int = adl_flag (Tag {})>
constexpr bool is_flag_usable (int) {
  return true;
}

template<class Tag>
constexpr bool is_flag_usable (...) {
  return false;
}

template<bool B, class Tag = detail::A>
struct dependent_writer : detail::writer<Tag> { };

template<
  class Tag = detail::A,
  bool    B = is_flag_usable<Tag> (0),
  int       = sizeof (dependent_writer<B>)
>
constexpr int f () {
  return B;
}

int main () {
  constexpr int a = f ();
  constexpr int b = f ();

  static_assert (a != b, "fail");
}
★★★★★

Последнее исправление: hateyoufeel (всего исправлений: 2)
Ответ на: комментарий от utf8nowhere

Нашёл забавную фишку про C++

Stateful metaprogramming через friend injection :-) Ого :-) Всего-то ВОСИМЬ ЛЕТ фишке :-)

Для C++ это совсем новая. Тут не все до сих пор на C++11 смогли перейти нормально.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от WatchCat

«Стройная система костылей и подпорок»(c)(r)(tm)

В шланге в последних версиях вроде как пофикшено.

А GCC стабильно генерирует

       xor     eax, eax
       ret

А что он по-твоему ещё должен генерировать?

hateyoufeel ★★★★★
() автор топика
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от seiken

Что такое «нормально перейти на С++11»?

Прочитать мануал и начать использовать в своём коде вместо того, чтобы писать на форумах «НИНУЖНО». Ну, вспомни @Iron_bug, например.

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

Айрон Баг - это пример воинствующего неприятия, причину которого я так и не понял. C++98 отстаёт по фичам даже от Ada2005.

seiken ★★★★★
()
Последнее исправление: seiken (всего исправлений: 1)

Этот код устарел и в новых компиляторах не работает. Однако, есть переделка под c++-20 https://mc-deltat.github.io/articles/stateful-metaprogramming-cpp20 и целая библиотечка, основанная на этом приёме https://github.com/deepgrace/smp. Ищется всё это в гугле по запросу stateful metaprograming.

P.S. Прикольно, не далее как вчера искал материалы по возможности сделать счётчик, а сегодня эту тему увидел.

JaM
()

Больше C++11 не надо.

Ну ладно, в C++14 использовать {} и записи чисел вида 1'200'387 – удобно.

Ну ладно, в C++17 std::string_view, std::variant, std::optional – тоже не плохие.

Но зачем весь остальной кошмар?

zx_gamer ★★★
()

разные части стандарта могут прямо друг другу противоречить

нет не могут...

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

а где ты взял гарантию иного? — где в стандарте указано, что должно быть только одно значение возвращаться?

constexpr bool is_flag_usable (...)

вот тут ничего странного в прототипе функции не видишь?

safocl ★★
()
Ответ на: комментарий от safocl

constexpr bool is_flag_usable (...)

вот тут ничего странного в прототипе функции не видишь?


хотя не — меня тут смутил элипсис — но с вариадик шаблонными аргами тоже самое...

safocl ★★
()

в общем по предварительным умозаключениям

sizeof (dependent_writer<B>)
может выполняться «лениво» в констэкспр контексте — тоесть компилятор может просто смотреть на содержание структуры (или класса) вычисляя размер, без полной инстанциации шаблонных классов, если от шаблонного аргумента не зависит как раз размер этого класса (структуры). И вроде эта ленивость unspecified (тоесть отдана на откуп компиляторам). — Если это на самом деле так (я пока не нашел четкого коментария из стандарта), то все компиляторы правы — и clang++, и g++, и даже msvc (который действует как g++).
А пока не будет инстанциирована эта dependent_writer<B> структура — не будет инстанции рована и функция adl_flag внутри структуры writer — а это значит что будет выбираться только перегрузка функции is_flag_usable с элипсисом (как и делает clang++).

safocl ★★
()
Последнее исправление: safocl (всего исправлений: 1)