Правильно писать конструктор на основе диапазона

70
7

У меня возник вопрос, связанный с написанием конструктора, основанного на диапазоне, для класса, но не смог найти правильную формулировку для поиска справки по Google.

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

// foo.h
#ifndef FOO_H
#define FOO_H

#include <iostream>

class Foo {
public:
Foo() {
std::cout << "Default constructor called" << std::endl;
}
template<class InputIterator> Foo(InputIterator first, InputIterator last) {
std::cout << "Range based constructor called" << std::endl;
}

Foo(size_t n, int val) {
std::cout << "size_t, int constructor called" << std::endl;
}

};

#endif // FOO_H

и иметь файл cpp

#include <iostream>
#include <vector>
#include "foo.h"

using std::cout; using std::endl;

int main() {
std::vector<int> v(10, 5);
Foo f_default;
Foo f_range(v.begin(), v.end());
Foo f_int(314, 159); // want this to call size_t, int ctctr
return 0;
}

В третьей строке в основном мы создаем Foo f_int(314, 159) который интуитивно мы хотим назвать конструктором size_t, int. Однако он сопоставляет генератор шаблонов вместо диапазонов. Есть ли способы, подобные этому, рассматриваются в C++? Я чувствую, что неправильно обрабатываю конструкторы на основе диапазона.

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

Пример, когда такая ситуация может произойти, заключается в том, что мы пишем векторный класс, в котором есть конструктор, основанный на значении size_t и по умолчанию (который будет templatised для класса, но я упрощен здесь) и другого конструктора на основе диапазонов итераторов.

спросил(а) 2017-04-01T07:42:00+03:00 3 года, 6 месяцев назад
1
Решение
99

Шаблон конструктора лучше подходит в третьем случае, потому что вы передаете два аргумента int, а Foo(size_t n, int val) требует преобразования int в size_t для первого аргумента. Если вы измените свой код на

Foo f_int(static_cast<size_t>(314), 159);

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

template<class InputIterator,
class = std::enable_if_t<
std::is_base_of<
std::input_iterator_tag,
typename std::iterator_traits<InputIterator>::iterator_category
>{}
>
>
Foo(InputIterator first, InputIterator last) {
std::cout << "Range based constructor called" << std::endl;
}

Если вы посмотрите на таблицу здесь, все итераторы, которые вы можете прочитать, это InputIterators или производные типы. Таким образом, приведенный выше код проверяет, что тип InputIterator, передаваемый конструктору, имеет этот тип или что-то, полученное от этого типа.

ответил(а) 2017-04-01T08:19:00+03:00 3 года, 6 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема