Нет подходящего конструктора для инициализации класса вариативного шаблона

Я пытаюсь написать класс-оболочку для декартова итератора продукта, предоставленного Мирандой Конрадо (исходный код можно найти на Гитхаб). Для удобства я также приведу здесь соответствующие фрагменты кода.

Мой класс можно построить двумя способами: один простой, просто перенаправляя контейнеры в конструктор product_iterator, другой немного сложнее: он берет несколько кортежей, описывающих linspace, необходимое для создания контейнеров, а затем конструирует итератор из их. Именно здесь я зашел в тупик.

Вот некоторый код. Во-первых, некоторые соответствующие заголовки из class product_iterator Конрадо:

// product_iterator.hpp

template <class... Containers>
class product_iterator:
    ...

    public:
      product_iterator();

      product_iterator(product_iterator const& other);

      product_iterator(Containers const&... containers);

      ~product_iterator();

      product_iterator const& operator=(product_iterator const& other);

     ....
};

template <class... Containers>
product_iterator<Containers...>
make_product_iterator(Containers const&... containers) {
  return product_iterator<Containers...>(containers...);
}

И вот мой класс:

// gridsearch.hpp

typedef std::unordered_map<std::string, Real> result_type;
typedef std::vector<result_type> resultgrid_type;


template <class... Containers>
class GridSearchIterator {
    typedef std::array<std::string,
            std::tuple_size<std::tuple<Containers...> >::value> 
            argname_type;

public:
    GridSearchIterator() : product_it(product_iterator<Containers...>()), 
                           argnames(argname_type()) {}

    GridSearchIterator(const argname_type& names, 
                       const Containers& ...containers);

    template <class... TupleTypes>
    static GridSearchIterator<Containers...> 
                                    initWith(const TupleTypes&& ...tuples);

    template<class F, class... Args>
    decltype(auto) iterate(F func, Args&&... params);

private:
    template <typename TupleType, size_t... Is>
    void product_impl(TupleType&& tuples, std::index_sequence<Is...>);
    template <typename TupleType>
    const auto& unpack_tuple(TupleType& t, size_t index);

    product_iterator<Containers...> product_it;
    argname_type argnames;
};

// implementation:

template <class... Containers>
GridSearchIterator<Containers...>::GridSearchIterator(
                                          const argname_type& names, 
                                          const Containers& ...containers):

                product_it(product_iterator<Containers...>(containers...)),
                                                        argnames(names) {}

template <class... Containers>
template <typename... TupleTypes>
GridSearchIterator<Containers...> GridSearchIterator<Containers...>::initWith(const TupleTypes&& ...tuples) 
{
    GridSearchIterator<Containers...> gsi = 
                                       GridSearchIterator<Containers...>();
    gsi.product_impl(std::tuple<TupleTypes...>(tuples...), 
                     std::index_sequence_for<TupleTypes...>{});
    return gsi;
}

template <class... Containers>
template <typename TupleType, size_t... Is>
void GridSearchIterator<Containers...>::product_impl(TupleType&& tuples, 
                                              std::index_sequence<Is...>) 
{
    product_it = product_iterator<Containers...>(
                                unpack_tuple(std::get<Is>(tuples), Is)...); 
// this is where the problem is; Compiler claims No matching constructor for initialization of 'product_iterator...
}

template <class... Containers>
template <typename TupleType>
const auto& GridSearchIterator<Containers...>::unpack_tuple(TupleType &t, 
                                                            size_t index) 
{
    std::string argname;
    auto left(0), right(0);
    Size step;
    std::tie(argname, left, right, step) = t;
    argnames[index] = argname;
    auto vec = linspace(left, right, step);
    return static_cast<const decltype(vec) &>(vec);
}

Функция linspace выше возвращает вектор чисел от left до right, равномерно распределенных по количеству steps. Это эквивалентно функции Numpy np.linspace.

Я проверил, и вызов unpack_tuple() действительно создает векторы, необходимые для инициализации product_iterator, но компилятор не согласен. Я предполагаю, что типы, возвращаемые unpack_tuple(), несколько отличаются от того, что ожидает конструктор product_iterator, но я не могу понять, в чем проблема. Или, может быть, проблема на самом деле заключается совсем в другом.

Для лучшего понимания, вот как я использую класс:

{
...
    typedef std::tuple<std::string, int, int, size_t> inttuple;
    typedef std::tuple<std::string, double, double, size_t> realtuple;
    typedef std::vector<int> intvector;
    typedef std::vector<Real> realvector;

    inttuple sidespan = std::make_tuple("side",1,1,1);
    real tuple takeprofit = std::make_tuple("takeprofit",1.,2.,2);
    real tuple stoploss = std::make_tuple("stoploss", -1.,-3.,3);
    inttuple period = std::make_tuple("horizon", 100, 100, 1);

    auto grid_iter = GridSearchIterator<intvector, realvector, realvector, intvector>
                                        ::initWith(std::forward<inttuple>(sidespan),
                                                   std::forward<realtuple>(takeprofit),
                                                   std::forward<realtuple>(stoploss),
                                                   std::forward<inttuple>(period));
...
}

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

ОБНОВЛЕНИЕ
Извините, я думал, что обновил свой вопрос вчера, но изменения по какой-то причине не были сохранены. Во всяком случае, @ max66 ответил на вопрос даже без дополнительной информации. Тем не менее, для полноты картины, вот определение linspace()

template <typename T>
std::vector<T> linspace(T a, T b, size_t N)

и сообщение компилятора:

В файле, включенном из /.../main.cpp:17:
/.../gridsearch.hpp:98:18: ошибка: нет подходящего конструктора для инициализации 'product_iterator<std::__1::vector<int, std::__1::allocator<int> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<int, std::__1::allocator<int> > >' product_it = product_iterator<Containers...>(unpack_tuple(std::get<Is>(tuples), Is)...);

/.../gridsearch.hpp:91:9: примечание: при создании экземпляра шаблона функции здесь запрашивается специализация 'GridSearchIterator<std::__1::vector<int, std::__1::allocator<int> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<int, std::__1::allocator<int> > >::product_impl<std::__1::tuple<std::__1::tuple<std::__1::basic_string<char>, int, int, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, double, double, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, double, double, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, int, int, unsigned long> >, 0, 1, 2, 3>' gsi.product_impl(std::tuple<TupleTypes...>(tuples...), std::index_sequence_for<TupleTypes...>{});

/.../main.cpp:90:88: примечание: при создании экземпляра шаблона функции здесь запрашивается специализация 'GridSearchIterator<std::__1::vector<int, std::__1::allocator<int> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<double, std::__1::allocator<double> >, std::__1::vector<int, std::__1::allocator<int> > >::initWith<std::__1::tuple<std::__1::basic_string<char>, int, int, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, double, double, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, double, double, unsigned long>, std::__1::tuple<std::__1::basic_string<char>, int, int, unsigned long> >' auto grid_iter = GridSearchIterator<intvector, realvector, realvector, intvector>::initWith(std::forward<inttuple>(sidespan),

В файле из /.../main.cpp:17:
В файле из /.../gridsearch.hpp:22: /.../product_iterator.hpp:73:7: примечание: конструктор-кандидат нежизнеспособно: неизвестно преобразование из 'vector<int, allocator<int>>' to 'const vector<double, allocator<double>>' для 2-го аргумента product_iterator(Containers const&... containers);


person seiji594    schedule 11.05.2019    source источник
comment
однако компилятор не согласен -- это расплывчато и включает интерпретацию/предположения с вашей стороны. Что делает оператор в значительной степени бесполезным для помощи в отладке. Каково точное сообщение от компилятора?   -  person JaMiT    schedule 11.05.2019
comment
Пожалуйста, предоставьте прототип функции linspace и полную ошибку компилятора   -  person Nellie Danielyan    schedule 11.05.2019


Ответы (1)


Трудно проверить/подтвердить/предложить правильный код, если вы не предлагаете полный пример.

В любом случае у вас есть ошибка ("нет соответствующего конструктора") в этой строке

    product_it = product_iterator<Containers...>(
                                unpack_tuple(std::get<Is>(tuples), Is)...); 

где, если я правильно понимаю, Containers... — это intvector, realvector, realvector, intvector, также известный как std::vector<int>, std::vector<Real>, std::vector<Real>, std::vector<int> (где, я полагаю, Real — это псевдоним для double).

Единственный вариативный конструктор для product_iterator - это тот, который получает Containers const&... containers, поэтому я полагаю, что это тот, который вы хотите сопоставить.

Мне кажется, что проблема в том, что unpack_tuple()

template <class... Containers>
template <typename TupleType>
const auto& GridSearchIterator<Containers...>::unpack_tuple(TupleType &t, 
                                                            size_t index) 
{
    std::string argname;
    auto left(0), right(0);
    Size step;
    std::tie(argname, left, right, step) = t;
    argnames[index] = argname;
    auto vec = linspace(left, right, step);
    return static_cast<const decltype(vec) &>(vec);
}

возвращать всегда intVector const & (std::vector<int> const &). Также при вызове с realVector (я полагаю, std::vector<double>).

Это (если я не ошибаюсь) потому, что вы определяете left и right как auto и инициализируете их int

auto left(0), right(0);

вы получаете пару int также, когда TupleType содержит Real элементов во второй и третьей позиции.

Итак, когда вы получите vec

auto vec = linspace(left, right, step);

вы получаете (я полагаю) std::vector<int>; Когда-либо; также, когда вы должны получить std::vector<Real>.

Предложение: определите left и right с правильным типом, который зависит от TupleType.

На примере (внимание: код не тестировался)

using lr_type = typename std::tuple_element<1u, TupleType>::type;

lr_type  left, right;

Начиная с С++ 14 вы можете использовать std::tuple_element_t, а `использование можно упростить следующим образом

using lr_type = std::tuple_element_t<1u, TupleType>;

Если вы можете использовать C++17, вы можете использовать структурированное связывание, и все станет намного проще.

template <typename TupleType>
const auto& GridSearchIterator<Containers...>::unpack_tuple(TupleType &t, 
                                                            size_t index) 
{
    auto [argname, left, right, step] = t;
    argnames[index] = argname;
    auto vec = linspace(left, right, step);
    return static_cast<const decltype(vec) &>(vec);
}

Не по теме: вы уверены, что это хорошая идея unpack_tuple() возвращать const ссылку на значение, которое уничтожается в конце выполнения метода?

person max66    schedule 12.05.2019
comment
@ max66Да, именно поэтому он не скомпилировался. После предложенных вами изменений все работает. Большое спасибо! Что касается вашего последнего комментария: на самом деле это артефакт моих отчаянных попыток решить проблему. Моя первоначальная реализация заключалась в том, чтобы просто вернуть rvalue, что я и собираюсь использовать. - person seiji594; 13.05.2019