2013-05-31 5 views
0

Я хотел создать простой вспомогательный алгоритм, который заполнил бы контейнер, например 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 в этом случае).

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

Уверен, что мне не хватает чего-то очевидного - я ценю ваше терпение и помощь.

Edit (ответ)

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

#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 

ответ

5

первая проблема - это то, что вы забыли, что 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) { 
//    ^^ 
// ... 
} 

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

В качестве альтернативы вы можете использовать вариативные шаблоны as suggested by KerrekSB in his answer (но это все равно не помешает вам передать экземпляр любого другого типа шаблонов, даже неконтейнерных).

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

progression(r, 10, 0.8, 10); 

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

progression(r, 10.0, 0.8, 10); 

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

+0

Спасибо! Это решает проблему. – Escualo

+0

У меня есть улучшенная версия этого ответа, но мое сетевое соединение опустилось, и я не могу редактировать его со своего мобильного телефона (это просто беспорядок). –

+0

@ Арриета: Рад, что это помогло! –

1

Причиной этого является недостаток в том, что вы говорите, две средние арг являются такого же типа, когда они на самом деле нет. Например, средняя - это float, а другая - int. В основном вы говорите, что a и коэффициент одного типа, но в вызове они разные типы.

+0

Я думаю, вы имели в виду, что середина - это «двойная», а другая - перед ней. – chris

+0

Посмотрите мои правки - это не проблема. Даже если я передаю два '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' – Escualo

+0

@aaronman - на самом деле проблема была - после того, как я удалил первую ошибку, ошибка, которую вы описали, появилась. – Escualo

4

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

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

+0

Это не вариационный шаблон – aaronman

+1

@aaronman: Никогда не утверждал, что это было ?! –

+0

Я никогда не думал об этом. Это аккуратно. – chris

Смежные вопросы