2012-06-18 27 views
10

Pracuję z biblioteką, która udostępnia interfejs do pracy. Jedną z funkcji tej biblioteki jest tak:Przekazywanie zmiennej jako argumentu szablonu

template <int a> 
void modify(){} 

muszę zmodyfikować parametry od 1 do 10 czyli rozmowy modify z argumentami szablonu od 1 do 10. W przypadku, że napisałem ten kod (podstawowa wersja kod, rzeczywisty kod jest znacznie większy).

for(int i=0; i<10; i++){ 
    modify<i>(); 
} 

Na kompilacji otrzymuję następujący błąd

error: 'i' cannot appear in constant-expression 

po przejściu przez kilka linków w internecie, poznałem, że nie mogę przekazać żadnej wartości jako szablon argument, który nie jest analizowany w czasie kompilacji . Moje pytanie jest następujące: 1. Dlaczego kompilator nie może oszacować wartości i podczas kompilacji? 2. Czy istnieją inne cele, które można osiągnąć bez zmieniania interfejsu API?


Jest jeszcze jedna rzecz, którą chcę zrobić. Call modyfikuj jako modyfikuj, gdzie VAR jest wynikiem niektórych funkcjonalnych obliczeń. Jak mogę to zrobić?

Odpowiedz

20

Jaka jest wartość i (która nie jest stała) w czasie kompilacji? Ther nie jest żadnym sposobem do anser, chyba że wykona pętlę. Ale wykonywanie nie "kompiluje" Ponieważ nie ma odpowiedzi, kompilator nie może tego zrobić.

Szablony nie są algorytmami do wykonania, ale makra do rozbudowy w celu wygenerowania kodu. Co można zrobić, to liczyć na specjalizacji wdrożyć iteracji przez rekursji, jak tutaj:

#include <iostream> 

template<int i> 
void modify() 
{ std::cout << "modify<"<<i<<">"<< std::endl; } 

template<int x, int to> 
struct static_for 
{ 
    void operator()() 
    { modify<x>(); static_for<x+1,to>()(); } 
}; 

template<int to> 
struct static_for<to,to> 
{ 
    void operator()() 
    {} 
}; 


int main() 
{ 
    static_for<0,10>()(); 
} 

pamiętać, że robiąc to, jesteś w rzeczywistości instancji 10 funkcji nazwanych modyfikują < 0> ... zmodyfikuj < 9>, wywoływane odpowiednio przez static_for < 0,10> :: operator() ... static_for < 9,10> :: operator().

Powtórzenie kończy się, ponieważ static_for < 10,10> zostanie utworzony z poziomu specjalności, która przyjmuje dwie identyczne wartości, która nie robi nic.

+0

+1. Zamierzałem zamieścić prawie tę samą odpowiedź. – Nawaz

+0

@Emilio Czy istnieje sposób na wywołanie modyfikacji gdzie VAR jest zmienną zwracaną przez niektóre obliczenia funkcjonalne, powiedzmy VAR = f()? – gibraltar

+0

@gibraltar: Tylko jeśli jest to C++ 11, a funkcja jest oznaczona jako "constexpr" (niż możesz po prostu napisać 'modify '). Zauważ, że zasady dotyczące constexpr są dość surowe. –

2
  1. "Dlaczego nie można oceniać i kompilator w czasie kompilacji?"

    To pokonałoby cel szablonów. Szablony istnieją dla przypadku, w którym kod źródłowy wygląda tak samo dla niektórych zestawów przypadków, ale instrukcje, które kompilator musi wygenerować, są za każdym razem inne.

  2. "Czy istnieją inne cele, które można osiągnąć bez zmieniania interfejsu API?"

    Tak, spójrz na Boost.MPL.

    Podejrzewam jednak, że właściwą odpowiedzią jest to, że chcesz zmienić interfejs API. Zależy to od funkcji wewnętrznych funkcji modify. Wiem, że masz jego źródło, ponieważ szablony muszą być zdefiniowane w nagłówkach.Zobacz więc, dlaczego musi on wiedzieć, i w czasie kompilacji, a jeśli nie, to najlepiej zastąpić (lub uzupełnić, jeśli chcesz zachować zgodność wsteczną) z normalną funkcją z parametrem.

+0

Czy możesz opracować na temat wykorzystania Boost.MPL, proszę? Nie znam tej biblioteki. – gibraltar

+0

@gibraltar: Ja też nie; po prostu przeczytaj dokumentację. –

2

Ponieważ poprosiłeś o odpowiedź za pomocą Boost.MPL:

#include <boost/mpl/for_each.hpp> 
#include <boost/mpl/range_c.hpp> 

#include <iostream> 

template <int N> 
void modify() 
{ 
    std::cout << N << '\n'; 
} 

// You need to wrap your function template in a non-template functor 
struct modify_t 
{ 
    template <typename N> 
    void operator()(N) 
    { 
     modify<N::value>(); 
    } 
}; 

int main() 
{ 
    namespace mpl = boost::mpl; 

    mpl::for_each< mpl::range_c<int,0,10> >(modify_t()); // prints 0 to 9 
} 
1

Bez użycia struct lub Rozreklamują może też być wykonana:

#include <iostream> 
#include <utility> 

template <int a> 
void modify() 
{ 
    std::cout<<a<<","; 
} 

template<int i,size_t... t> 
constexpr inline void CT_for_impl(std::integer_sequence<size_t,t...>) 
{ 
    bool kai[]= { (modify<i+t>(), false)...}; 
} 

template<int i,int n> 
constexpr inline void CT_for() 
{ 
    CT_for_impl<i>(std::make_index_sequence<n-i+1>()); 
} 

int main() 
{ 
    CT_for<-5,5>(); 
    return 0; 
} 
1

rozwiązanie błędu: "i" nie może występować w stałym wyrażeniu dla powyższego problemu

To read about constexpr click this link

#include <iostream> 
using namespace std; 

template <typename T> 
void modify(T a) 
{ 
    cout<<a<<endl; //to check if its working 
} 


//func converts int a into const int a 
constexpr int func(int a) 
{ 
    return a; 
} 

int main(){ 
    for(int i=0; i<10; i++){ 
     modify(func(i));//here passing func(i) returned value which can be used as template argument now as it is converted to constexpr  
} 
    return 0; 
} 
+0

To nie jest to, co zamierzał OP. Po pierwsze, sprawianie, że liczba całkowita w czasie wykonywania właśnie przekazuje ją do odpowiedniej funkcji, nie działa ... po prostu nie może. Ale nawet gdyby tak było, nazwałbyś 'modify ', a nie wersje nietypowe, o które prosił OP. – davidhigh

+0

Ale szablon zostanie automatycznie wykryty – SSD

0

Biorąc chcesz wywołać funkcji w czasie wykonywania przez ich indeksu i nie można zmienić API, można rozważyć typu wymazywania:

std::vector<std::function<void(int)> > func; 
func.push_back(modify<1>); 
func.push_back(modify<2>); 
//... and so on ... 
func.push_back(modify<10>); 

for(int i=0; i<10; ++i) 
{ 
    func[i](); //calls modify<i+1>(); 
} 

Kilka punktów, o których warto wspomnieć:

  • Nie do tego służą szablony, ale jest to sposób na Tatic Library do świata run-time. Podstawowym wymaganiem jest to, że pracujemy z typami homogenicznymi (- if modify<7>() powróciłby, powiedzmy, std::string, gdyby całe podejście się zepsuło).
  • Poprzednie rozwiązanie z usuwaniem typu ma dodatkowy narzut. Można go uzyskać szybciej za pomocą wskaźników funkcji, ale nadal będzie wolniej niż wywoływanie funkcji w czasie kompilacji.
  • Można (i należy) również zawinąć push_back s do innej iteracyjnej funkcji statycznej, aby uniknąć wywołań ręcznych.
Powiązane problemy