0

Как создать конструктор, позволяющий использовать braced-init-list?

9

У меня есть класс Phenotype со следующим конструктором:

Phenotype(uint8 init[NUM_ITEMS]);

Я могу создать объект Phenotype следующим образом:

uint8 data[] = {0, 0, 0, 0, 0};
Phenotype p(data);

Однако, когда я пытаюсь создать объект так:

Phenotype p = {0, 0, 0, 0, 0};

я получаю ошибку. Вывод:

$ make
g++ -Wall -g main.cpp -std=c++0x
main.cpp: In function ‘int main(int, char**)’:
main.cpp:109: error: no matching function for call to ‘Phenotype::Phenotype(<brace-enclosed initializer list>)’
main.cpp:37: note: candidates are: Phenotype::Phenotype(uint8*)

Ошибка, похоже, указывает на то, что существует способ определить конструктор, который принимает инициализатор с фигурными скобками. Кто-нибудь знает, как это можно реализовать?

2 ответ(ов)

0

Это можно сделать только для агрегатов (массивов и некоторых классов). Напротив распространенного мнения, это также работает для многих классов, не являющихся POD. Однако, написать конструктор, который принимает их, невозможно.

Поскольку вы отметили это как "C++0x", это теперь возможно. Магические слова — "конструктор с инициализатором списка". Пример выглядит следующим образом:

Phenotype(std::initializer_list<uint8_t> c) {
  assert(c.size() <= std::size(m_array));
  std::copy(c.begin(), c.end(), m_array);
}

// используется так
Phenotype p1{1, 2, 3};
Phenotype p2({1, 3, 2}); // тоже работает
Phenotype p3(1, 2, 3); // не работает

Однако такая инициализация будет по умолчанию создавать массив, а затем использовать оператор присваивания. Если ваша цель — скорость и безопасность (вы получаете ошибки компиляции при слишком большом количестве инициализаторов!), вы также можете использовать обычный конструктор с вариативным шаблоном.

Это может быть более общим, чем нужно (часто инициализатор списка вполне достаточен, особенно для простых целых чисел). Он использует "перфектное пересылание", так что аргумент rvalue может быть перемещен в элемент массива:

template<typename ...T>
Phenotype(T&&...t): m_array{ std::forward<T>(t)... } {

}

// используется так
Phenotype p1{1, 2, 3}; 
Phenotype p2(1, 2, 3); // тоже работает
Phenotype p3({1, 2, 3}); // не работает   

Это сложный выбор!

Правка: В последнем примере всё же работает, поскольку конструктор не был объявлен explicit, так что он может использовать конструктор копирования Phenotype, создавая временный объект Phenotype и копируя его в p3. Но именно так мы и не хотели бы, чтобы эти вызовы выглядели 😃

0

Вам нужно использовать шаблонный тип std::initializer_list. Вот пример:

#include <iostream>

class X {
public:
    X(std::initializer_list<int> list) {
        for (auto i = list.begin(); i != list.end(); i++) {
            std::cout << *i << std::endl;
        }
    }
};

int main() {
    X x = {1, 2, 3, 4, 5};
}

В этом коде класс X имеет конструктор, который принимает std::initializer_list<int>. Это позволяет вам инициализировать объект x с помощью списка инициализации, который состоит из целых чисел. В конструкторе мы проходим по элементам списка и выводим их на экран.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь