Как предотвратить неправильные значения в списке инициализатора? - PullRequest
1 голос
/ 16 февраля 2020

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

class a
{
  int b;
public:
  a(int c)
  {
    if(c < MIN_ALLOWED || c > MAX_ALLOWED)
    {
      // Take some measure
    }
    else
    {
      b = c;
    }
  }
};

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

class a
{
  const int b;
public:
  a(int c) : b(c)
  {
    // How to control "c" value?!...
  }
};

Ответы [ 5 ]

2 голосов
/ 16 февраля 2020

Сделать функцию:

class a {
    int const b;

public:
    a(int const c)
        : b { initializeB(c) }
    {
    }

private:
    static int initializeB(int const c)
    {
        if (c < MIN_ALLOWED || c > MAX_ALLOWED) {
            // Take some mesure
            return -1; // example
        }
        return c;
    }
};
1 голос
/ 16 февраля 2020
class a
{
    const int b;
public:
    a(int c) : b((c < MIN_ALLOWED || c > MAX_ALLOWED) ? throw std::logic_error("bad") : c)
    {
        // How to control "c" value?!...                                                                        
    }
};
1 голос
/ 16 февраля 2020

Вы можете делегировать проверку и изменение переменной функции.

class a {
 public:
  a(int c) : b(ValidateAndTransformInputParameter(c)) {}
 private:
  const int b;
  int ValidateAndTransformInputParameter(int d) const {
    if (d < 100) {
      d = 100;
    }
    return d;
  }
};
0 голосов
/ 16 февраля 2020

Все вышеприведенные ответы хороши и работают ... но чтобы мы не забыли, вы также можете сделать:

class a
{
    public:
        a(int c) : b([=]() -> int {
            if (c < MIN_ALLOWED || c > MAX_ALLOWED)
                throw std::logic_error("b");
            return c;
        }()) {}
    private:
        int b;

};

Хотя, на мой взгляд, это выглядит ужасно, наличие лямбды в списке инициализаторов, но это все еще возможно.

0 голосов
/ 16 февраля 2020

Существует три основных способа:

  1. Цепочка проверки с помощью оператора запятой:

    a(int c) : b(validate(c), c) {}
    

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

  2. Использование троичного оператора:

    a(int c) : b(validate(c) ? c : throw 0) {}
    

    Для этого требуется наименьшее количество паполола, если оно действительно одноразовое.

  3. Использование преобразующего и проверяющего класса или функции:

    a(int c) : b(process(c)) {}
    

Опционально, они могут быть объединены с цепью ctor, особенно если больше, чем один результат получается при обработке входных данных:

private:
    struct process {
        ...
    };
    a(process x) : b(x.b), c(x.c), d(x.d) {}
public:
a(int c) : a(process(c)) {}
...