Как я могу заставить метод класса C ++ принимать только несколько целочисленных литералов? - PullRequest
3 голосов
/ 22 сентября 2011

Я занимаюсь рефакторингом класса, который в настоящее время имеет метод:

void resize(size_t sz)

В текущей кодовой базе sz всегда равно 0,1,2 или 3. Базовый класс меняется от динамического выделения к предварительно выделенному массиву maxsize == 3.

Как я могу получить ошибку времени сборки, если кто-то пытается изменить размер до sz> 3? Достаточно просто добавить проверку во время выполнения. Но я бы предпочел получить проверку во время компиляции, которая не удалась раньше.

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

x.resize(2)

должен по-прежнему компилироваться как есть.

Но если кто-то придет и попытается

x.resize(4)
  or
x.resize(n)

он не должен компилироваться или ссылаться.

Я думал о шаблоне, специализированном для int, который не определен ни для чего, кроме {0,1,2,3}. Но я не совсем уверен, как заставить его делать то, что я хочу, в рамках стандарта c ++.

редактирование:

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

например. Я думал что-то вроде

void resize( ConstructedFromLiteral<0,3> sz)

или

void resize( ConstructedFromLiteral<0> sz)
void resize( ConstructedFromLiteral<1> sz)
void resize( ConstructedFromLiteral<2> sz)
void resize( ConstructedFromLiteral<3> sz)

Ответы [ 5 ]

6 голосов
/ 22 сентября 2011

Нет способа получить время компиляции для проверки значение времени выполнения .Представьте себе, если вы сказали:

resize(read_some_number_from_disk());

Как компилятор должен это проверять?

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

class Foo
{
  template <unsigned int N> void resize()
  {
    static_assert(N < 4, "Error!");
    //...
  }

  //...

};

Если у вас нет статических утверждений, вы можете создать свой собственный класс static-assert, который не будет компилироваться:

template <bool> struct ecstatic_assert;  // no definition!
template <> struct ecstatic_assert<true> { } ;

Использование:

... resize ... { ecstatic_assert<N < 4> ignore_me; /* ... */ } 
3 голосов
/ 22 сентября 2011

Я бы использовал static_assert, чтобы проверить это во время компиляции:

struct foo {
  template <int N> 
  void resize() {
    static_assert(N >= 0 && N < 4);
  }
};

Вы получаете static_assert встроенный в C ++ 11, но достаточно легко реализовать и в C ++ 03 . По сравнению со специализациями это избавляет вас от довольно утомительного дублирования кода.

1 голос
/ 22 сентября 2011

Вы не можете сделать это.Вы не можете сохранять вызовы функций для изменения размера как есть, и проверять n во время компиляции, поскольку это значение времени выполнения.Вам нужно будет реорганизовать ваш код, чтобы получить ошибку времени компиляции (например, std / boost static_assert).

0 голосов
/ 22 сентября 2011

Мне не нравится этот ответ по понятным причинам, но он удовлетворяет вашим заявленным требованиям:

struct x {
  void true_resize(int n) { }
  template <int N> void template_resize();
};
  template<> void x::template_resize<0>() { true_resize(0); }
  template<> void x::template_resize<1>() { true_resize(1); }
  template<> void x::template_resize<2>() { true_resize(2); }
  template<> void x::template_resize<3>() { true_resize(3); }
#define resize(x) template_resize<x>();


int main () {
  x x;
  x.resize(2);
  x.resize(4);
}

В случае, если это не очевидно, #define не подчиняется области действия C ++правила, и поэтому разрушает имя resize для всех других целей.

0 голосов
/ 22 сентября 2011

Вы можете сделать четыре открытых встроенных специализированных шаблона для целых {0, 1, 2, 3}.Это были бы простые однострочные функции, которые вызывают частную обычную обобщенную функцию для любого типа int.

EDIT: Для скептиков, которые говорят, что это невозможно сделать с помощью специализированных шаблонов (почемуthe downvotes?):

class Demo {
private:
    template<int MyInt>
    void PrivateFunction() {
        cout << MyInt << endl;
    }
public:
    template<int MyInt> void PublicFunction();
    template<> void PublicFunction<0>() { PrivateFunction<0>(); }
    template<> void PublicFunction<1>() { PrivateFunction<1>(); }
    template<> void PublicFunction<2>() { PrivateFunction<2>(); }
    template<> void PublicFunction<3>() { PrivateFunction<3>(); }
};

Попробуйте вызвать Demo :: PublicFunction <4> (), и вы получите ошибку компоновщика.Выполняет то же самое и полезно, если вы не имеете / не хотите создавать static_assert.

Как уже упоминалось, не так просто проверить значение параметра ...

...