2012-06-18 16 views
10

Estoy trabajando con una biblioteca que expone una interfaz para trabajar. Una de las funciones de esta biblioteca es así:Pasar una variable como argumento de plantilla

template <int a> 
void modify(){} 

tengo que modificar los parámetros del 1 al 10, es decir la llamada modify con argumentos de plantilla con de 1 a 10. Para que escribí este código (una versión básica de código, el código real es mucho más grande).

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

En la compilación recibo el siguiente error

error: 'i' cannot appear in constant-expression 

Después de pasar a través de algunos enlaces en internet, llegué a saber que no puedo pasar cualquier valor como parámetro de plantilla que no se evalúa en tiempo de compilación . Mi pregunta es la siguiente: 1. ¿Por qué el compilador no puede evaluar i en tiempo de compilación? 2. ¿Hay algún otro para lograr el objetivo que estoy tratando de lograr sin cambiar la interfaz API?


Hay otra cosa que quiero hacer. Llamar modificar como modificar donde VAR es el resultado de algún cálculo funcional. ¿Cómo puedo hacer eso?

Respuesta

20

¿Cuál es el valor de i (que no es una constante) en el tiempo de compilación? No hay forma de responder a menos que se ejecute el ciclo. Pero ejecutar no es "compilar" Como no hay respuesta, el compilador no puede hacer eso.

Las plantillas no son algoritmos para ser ejecutados, sino macros que deben expandirse para producir código. Lo que puede hacer es confiar en la especialización de implementar iteración por recursión, como aquí:

#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>()(); 
} 

Tenga en cuenta que, al hacer esto, usted es, de hecho, una instancia de 10 funciones con nombre modificar < 0 ...> modifique < 9>, llamado respectivamente por static_for < 0,10> :: operator() ... static_for < 9,10> :: operator().

La iteración finaliza porque static_for < 10,10> se creará una instancia de la especialización que toma dos valores idénticos, que no hace nada.

+0

+1. Iba a publicar casi la misma respuesta. – Nawaz

+0

@Emilio ¿Hay alguna manera de llamar a modificar donde VAR es una variable devuelta por algún cálculo funcional, digamos VAR = f()? – gibraltar

+0

@gibraltar: solo si es C++ 11 y la función está marcada como 'constexpr' (que simplemente puede escribir' modificar '). Y tenga en cuenta que las reglas para constexpr son bastante estrictas. –

2
  1. "¿Por qué el compilador no puede evaluar i en tiempo de compilación?"

    Eso anularía el propósito de las plantillas. Las plantillas están ahí para el caso donde el código fuente se ve igual para algunos casos, pero las instrucciones que el compilador necesita generar son diferentes cada vez.

  2. "¿Hay alguna otra manera de lograr el objetivo que estoy tratando de lograr sin cambiar la interfaz API?"

    Sí, mira Boost.MPL.

    Sin embargo, sospecho que la respuesta correcta aquí es que desea cambiar la API. Depende de las funciones internas de la función modify. Sé que tienes su fuente, porque las plantillas deben definirse en los encabezados.Así que eche un vistazo por qué necesita saber i en tiempo de compilación y si no lo hace, sería mejor reemplazar (o complementar si necesita mantener la compatibilidad con versiones anteriores) con la función normal con parámetro.

+0

¿Puede explicar el uso de Boost.MPL por favor? No estoy familiarizado con esta biblioteca. – gibraltar

+0

@gibraltar: Tampoco soy yo; solo lea la documentación. –

2

Has solicitado que una respuesta utilizando 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

Sin utilizar estructura o Boost que también se puede hacer:

#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

solución al error: 'i' no puede aparecer en expresión constante para el problema anterior

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

Esto no es lo que el OP pretendía. En primer lugar, hacer un número entero en tiempo de ejecución consintiéndole simplemente pasarlo a una función correspondiente no funciona ... simplemente no puede. Pero incluso si lo hiciera, llamaría 'modify ', no las versiones sin tipo que solicitó el OP. – davidhigh

+0

Pero la plantilla se detectará automáticamente – SSD

0

Dado que quieren llamar a las funciones en tiempo de ejecución por su índice y no se puede cambiar la API, se puede considerar el tipo de borrado-:

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>(); 
} 

Algunos puntos para mencionar:

  • Eso no es para lo que las plantillas son principalmente, pero es una manera de llevar biblioteca tática para el mundo de tiempo de ejecución. El requisito básico para esto es que uno trabaja con tipos homogéneos (--si modify<7>() devolviera, digamos, std::string se rompería todo el enfoque).
  • La solución anterior que utiliza borrado de tipo tiene una sobrecarga. Uno puede quizás obtenerlo más rápido mediante el uso de punteros a funciones, pero igual será más lento que llamar a las funciones en tiempo de compilación.
  • Uno puede (y debería) también envolver el push_back s en otra función estática iterativa para evitar las llamadas manuales.
Cuestiones relacionadas