Складывать утверждения в классе C++?

Итак, в ситуации неклассного типа я могу сделать что-то вроде этого:

int val_to_check = 0;

int some_func(int param) {
  assert(val_to_check == 0);
  return param*param+param;
}

int main() {
  printf("Val: %i\n", some_func(rand()));
  return 0;
}

Если val_to_check объявлен const вместо этого утверждение может быть свернуто компилятором.

Мне любопытно, возможно ли получить подобное постоянное сворачивание с помощью переменной-члена класса. Например, я могу сделать что-то вроде:

class Test {
public:
  Test(int val) : val_(val) {}
  int some_func(int param) {
     assert(val_ == 0);
     return param*param+param;
   }
private:
  const int val_;
};

Поэтому val_ должен быть известен, когда класс определен, а-ля:

  Test my_test(0);
  printf("Val: %i\n", my_test.some_func(rand()));

(Я знаю, что это надуманные примеры). Кажется, что иногда можно было бы свернуть утверждения, но простые примеры, которые я тестировал, похоже, этого не делают. Лучшее, что я получил, - это перемещение кода подтверждения в конец функции (при компиляции с помощью w/ -O3).

5 ответов

Решение

В приведенном вами примере класса компилятор не может предположить, что константа равна нулю, поскольку у вас есть две переменные времени выполнения:

  • Ваш const int val_ является константой только для каждого экземпляра класса, поэтому он никогда не может оптимизировать код функции класса, поскольку он должен обслуживать каждый случай.
  • Пример реализации не предоставляет буквальную константу, он предоставляет результат rand() который является переменным. Это может быть возможно для его оптимизации, если он знает ТОЛЬКО val_ когда-либо предоставленный всем экземплярам этого класса - ноль.

Вы пытались предоставить константу конструктору, чтобы увидеть, оптимизирует ли он ее?

В C++ существует понятие "константное выражение" (5.19). Это буквальное выражение, арифметическое выражение, включающее только константные выражения, или значение переменной, статически инициализированной константным выражением. Компилятор может определить значение такого выражения во время компиляции.

В твоем случае, val_ не является "константным выражением", поскольку оно может иметь разные значения в зависимости от объекта, членом которого он является. Для внеплановой версии some_funcВы, вероятно, согласны с тем, что компилятор не может знать, что это константа. Для встроенной версии можно было бы определить значение val_при условии, что у вас также есть полный исходный код всех проанализированных конструкторов. Способность компилятора выполнить этот анализ - вопрос качества реализации - ваш компилятор, по-видимому, не способен.

-O2, кажется, делает трюк для меня:

% cat foo.cxx

 #include <cstdio>
 #include <cstdlib>
 #include <cassert>

 class Test {
   public:
     Test(int val) : val_(val) {}
     int some_func(int param) {
       assert(val_ == 0);
       return param*param+param;
     }
   private:
     const int val_;
 };

 int main() {
   Test my_test(0);
   printf("Val: %d\n", my_test.some_func(rand()));
   return 0;
 }

 % g++ -S foo.cxx && grep assert foo.s ; echo $?
         .ascii "%s:%u: failed assertion `%s'\12\0"
 0

 % g++ -O2 -S foo.cxx && grep assert foo.s ; echo $?
 1

Если вы хотите утверждать, что вы уверены, что его свернули, вы, возможно, захотите посмотреть boost::static_assert.

Прямо сейчас, я довольно обеспокоен основным понятием дизайна: вы позволяете пользователю класса предоставить значение, но затем утверждаете, что это должно быть одно конкретное значение. Если верна только одна ценность, почему они вообще ее поставляют? Почему бы просто не использовать правильное значение и покончить с этим?

Имейте в виду, что общая идея утверждения состоит в том, что его неудача НЕ должна сигнализировать о чем-то вроде неправильного ввода. Он должен использоваться только для обозначения фундаментальной проблемы с самой программой - что где-то есть неисправная логика или что-то в этом порядке.

С другой стороны, возможно, вы сталкиваетесь с ситуацией, когда допускаются разные значения, но вы хотите предпринять разные действия (или реализовать одни и те же действия по-разному) для одного конкретного значения. Чтобы свернуть утверждение, очевидно, это должна быть константа времени компиляции.

В таком случае я бы рассмотрел создание шаблона с этим значением в качестве нетипового параметра шаблона. Специализируйте шаблон для значения, которое вы хотите реализовать специально. Это дает примерно тот эффект, который вам нужен, но усиливает тот факт, что тест для этого значения должен выполняться во время компиляции, а не во время выполнения. Вместо того, чтобы писать код времени выполнения и надеяться, что компилятор достаточно умен, чтобы справиться с ним во время компиляции, вы явно пишете совпадение времени компиляции.

val_ является const для экземпляра, а не для всех экземпляров. Если вы действительно хотите, чтобы он был одинаковым для всех экземпляров, то вы можете сделать его статическим константой, и это позволит компилятору оптимизировать его, что на самом деле и происходит в вашем первом примере с глобальным константным созданием.

Как примечание, ваши примеры могут быть переполнены целыми числами.

Другие вопросы по тегам