Не удалось вывести аргументы шаблона при использовании параметров шаблона шаблона

Я хотел создать простой вспомогательный алгоритм, который будет заполнять контейнер, такой как std::vector<T>с геометрической прогрессией (первый член aи n-й срок дается a * pow(r, n-1), где r это заданное соотношение); Я создал следующий код:

#include<vector>
#include<algorithm>
#include<iostream>

template<template <typename> class Container, typename T>
void progression(Container<T>& container, T a, T ratio, size_t N) {
if(N > 0) {
T factor = T(1);
for(size_t k=0; k<N; k++) {
container.push_back(a * factor);
factor *= ratio;
}
}
}

int main() {
std::vector<double> r;
progression(r, 10.0, 0.8, static_cast<size_t>(10));

for(auto item : r) {
std::cout<<item<<std::endl;
}

return 0;
}

который выдает следующие ошибки при попытке компиляции:

$ g++ geometric.cpp -std=c++11 # GCC 4.7.2 on OS X 10.7.4
geometric.cpp: In function ‘int main()’:
geometric.cpp:18:52: error: no matching function for call to ‘progression(std::vector<double>&, double, double, size_t)’
geometric.cpp:18:52: note: candidate is:
geometric.cpp:6:6: note: template<template<class> class Container, class T> void progression(Container<T>&, T, T, size_t)
geometric.cpp:6:6: note:   template argument deduction/substitution failed:
geometric.cpp:18:52: error: wrong number of template arguments (2, should be 1)
geometric.cpp:5:36: error: provided for ‘template<class> class Container’

Сообщение об ошибке Clang является более тонким:

$ clang++ geometric.cpp -std=c++11 # clang 3.2 on OS X 10.7.4
geometric.cpp:18:3: error: no matching function for call to 'progression'
progression(r, 10, 0.8, 10);
^~~~~~~~~~~
geometric.cpp:6:6: note: candidate template ignored: failed template argument deduction
void progression(Container<T>& container, T a, T ratio, size_t N) {
^
1 error generated.

Я бы ожидал, что используя параметры шаблона шаблона, я смогу вывести не только контейнер, но и контейнер value_type (T в этом случае).

Итак, вопрос в следующем: Как я могу создать универсальную функцию, которая сможет выводить как тип контейнера, так и тип значения?

Я уверен, что упускаю что-то очевидное — я ценю ваше терпение и помощь.

Следующий код ведет себя как ожидалось:

#include<vector>
#include<algorithm>
#include<iostream>

template<template <typename...> class Container, typename T, typename... Args>
void progression(Container<Args...>& container, T a, T ratio, size_t N) {
if(N > 0) {
T factor = T(1);
for(size_t k=0; k<N; k++) {
container.push_back(a * factor);
factor *= ratio;
}
}
}

int main() {
std::vector<double> r;
progression(r, 10.0, 0.8, 10);

for(auto item : r) {
std::cout<<item<<std::endl;
}

return 0;
}

Выход:

10
8
6.4
5.12
4.096
3.2768
2.62144
2.09715
1.67772
1.34218

0

Решение

первая проблема в том, что вы забыли, что std::vector<> это шаблон класса, принимающий два Параметры шаблона (тип элемента и распределитель), а не один. Тот факт, что второй параметр шаблона имеет значение по умолчанию, не имеет значения, когда вы используете параметры шаблона шаблона:

template<template <typename, typename> class Container, typename T, typename A>
//                           ^^^^^^^^                               ^^^^^^^^^^
void progression(Container<T, A>& container, T a, T ratio, size_t N) {
//                         ^^^^
// ...
}

Обратите внимание, что это сделает невозможным передачу, например, экземпляра std::map или же std::unordered_map в качестве первого аргумента функции. Поэтому я предлагаю отказаться выводя что первый аргумент является экземпляром стандартного контейнера (стандартные контейнеры просто не одинаковы):

template<typename C, typename T, typename A>
//       ^^^^^^^^^
void progression(C& container, T a, T ratio, size_t N) {
//               ^^
// ...
}

То, что вы можете сделать, это выразить время компиляции сдерживать, возможно через static_assert и на основе черты пользовательского типа, что C должен быть экземпляром стандартного контейнера.

Кроме того, вы можете использовать шаблоны как предложено KerrekSB в своем ответе (но это все равно не помешает вам передать экземпляр любого другого типа шаблона, даже неконтейнерного).

вторая проблема в том, как вы называете свой шаблон:

progression(r, 10, 0.8, 10);

Здесь тип второго аргумента intв то время как тип элемента контейнера double, Это запутает компилятор при выполнении вывода типа. Либо назови это так:

progression(r, 10.0, 0.8, 10);

Или позвольте вашему компилятору выводить другой тип для второго аргумента (возможно, SFINAE-ограничивает его тем, что может быть преобразовано в тип элемента).

5

Другие решения

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

template <template <typename...> class Container, typename ...Args>
void foo(Container<Args...> const & c)
{
// ...
}

(Специальный пункт действует, что это работает, даже когда Container это не вариационный шаблон.)

4

Причина, по которой это терпит неудачу, заключается в том, что вы говорите, что средние два аргумента имеют одинаковый тип, хотя на самом деле это не так. Например, середина — это число с плавающей точкой, а другая — целое число. В основном вы говорите, что a и ratio — это один и тот же тип, но в вызове они разные.

1
По вопросам рекламы [email protected]