2016-06-21 3 views
0

Я хотел бы вызвать функцию шаблона foo с последовательностью различных (нетиповых) параметров, взяв последовательные целочисленные значения между нижней и верхней границей. Например:Вызов функции шаблона с несколькими значениями параметров

template <int K> void foo(){ ... } 

int const UPPER = 10, LOWER = 5; 

for(int i = LOWER; i <= UPPER; i++) 
    foo<i>(); 

Это, конечно, не будет работать, так как i не известно во время компиляции. Я ищу способ достижения такого рода программы без необходимости писать что-то вроде:

foo<5>(); foo<6>(); foo<7>(); foo<8>(); foo<9>(); foo<10>(); 

Это, в частности, потому, что я намерен на изменение UPPER и LOWER от одного исполнения к другому.

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

int const arr[6] = {5, 6, 7, 8, 9, 10}; 

for(int i = LOWER; i <= UPPER; i++) 
    foo<arr[i]>(); 

Но, опять-таки, хотя элементы массива являются постоянными, i не известно, в compile-time так и не arr[i]. Какие-либо предложения?

Заранее спасибо.

+2

Похоже, что вы можете использовать [ 'зЬй: : integer_sequence'] (http://en.cppreference.com/w/cpp/utility/integer_sequence) – NathanOliver

+0

Вы можете поместить foo() в 'template struct foowrapper', что делает его намного проще .. Это разрешено ? – lorro

ответ

3

Вы можете использовать два шаблона и std::enable_if, чтобы выбрать один из них, в зависимости от того, является ли Lower равным Upper. В случае, если они являются равными, мы ничего не делаем. В противном случае мы вызываем foo<Lower>() и рекурсируем с параметрами Lower + 1 и Upper.

template <int Lower, int Upper> 
typename std::enable_if<Lower == Upper, void>::type callFoo() 
{ 

} 

template <int Lower, int Upper> 
typename std::enable_if<Lower != Upper, void>::type callFoo() 
{ 
    static_assert(Lower < Upper, "Lower must be less than or equal to Upper"); 

    foo<Lower>(); 
    callFoo<Lower + 1, Upper>(); 
} 

Учитывая этот шаблон, следующая строка будет вызывать foo<K>() для K значений 5, 6, 7, 8, 9, 10.

callFoo<5, 11>(); 
+0

Очень элегантное решение. Спасибо. – tmnol

+0

Из любопытства вы знаете какие-либо обходные пути, которые могли бы сделать этот код совместимым со старыми версиями C++ (до C++ 11)? – tmnol

0

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

+1

Дело в том, что ОП знает целые числа во время компиляции. Речь идет о эквиваленте цикла компиляции, который может использовать их в качестве аргументов шаблона. – chris

+0

Я знаю, но я не знал, что подобное существовало. @NathanOliver указал на то, о чем я не знал, std :: integer_sequence – MaciekGrynda

+0

@MaciekGrynda Если бы мне удалось передать целые числа в качестве аргументов функции, это действительно облегчило бы мою жизнь. Но я не могу по причинам, выходящим за рамки вопроса. – tmnol

3

Вы можете использовать std::integer_sequence, чтобы получить список во время компиляции чисел от 0 восходящих и затем добавьте ваше смещение:

// Here we take the lower bound and the sequence 0 to (Upper - Lower). 
// We call foo with each number in the sequence added to the lower bound. 
template<int Lower, int... Ints> 
void call_foo_with_range_helper(std::integer_sequence<int, Ints...>) { 
    // A common trick to expand the parameter pack without recursion or fold expressions 
    (void)std::initializer_list<int>{(foo<Lower + Ints>(), 0)...}; 
} 

// This simply makes it easier for the caller to use. 
// We take the lower and upper bounds only. 
template<int Lower, int Upper> 
void call_foo_with_range() { 
    call_foo_with_range_helper<Lower>(std::make_integer_sequence<int, Upper - Lower + 1>()); 
} 

int main() { 
    int const UPPER = 10, LOWER = 5; 

    call_foo_with_range<LOWER, UPPER>(); 
} 
+0

Благодарим вас за ответ.Я отметил ответ @TerraPass как можно лучше, так как мне было легче понять его (и я полагаю, что люди, читающие мой код тоже), но оба являются хорошими решениями. – tmnol

+2

@TudorManole, проблем нет. Весь бизнес 'initializer_list' является неудачным, и когда доступны краткие выражения, его можно заменить на' (foo (), ...); '. Кроме того, 'std :: integer_sequence' является одной из тех вещей, которые вам, как правило, нужно несколько раз, прежде чем вы действительно оцените их. В этом случае основная часть работы поддерживает любой диапазон, а не один, начиная с 0. – chris

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