2011-05-21 7 views
6

Esta pregunta se hizo a mí en una entrevista:plantilla para una función

Digamos que tiene una función que puede tomar cualquier tipo de argumentos y cualquier número de argumentos. ¿Cómo escribirías una función de plantilla para la misma?

No sé la respuesta exacta. ¿Alguien podría sugerir?

+1

¿Cuál fue su respuesta? –

+0

como dije no pude responder – Vijay

+2

en realidad siento ser un pedante, pero usted dijo "No sé la respuesta exacta". –

Respuesta

12

Comprobaron su conocimiento de la próxima estándar C++. La nueva característica se denomina "plantillas variadic" y se ve así:

template<typename... Args> void f(const Args&... args) 
{ 
    // do something 
} 

Durante más complicados ejemplos véase, por ejemplo, this tutorial.

+0

¿No significa eso que todos los argumentos serán del mismo tipo? –

+0

@George: no es así. – ybungalobill

+2

La lista de argumentos también podría ser 'Args && ...' para un reenvío perfecto. – Philipp

4

En C++ 03, para cualquier número de argumento en la plantilla de función no es posible. Sin embargo, para cualquier tipo de argumento puede escribir:

template<typename T, typename U> 
void f(const T & t, const U &u) 
{ 
    //... 
} 
+3

soy consciente de esto y esto no responde a mi pregunta – Vijay

+0

@Rahul: ¿Así que le diste la voz baja? porque lo sabes? – Nawaz

2

Estoy de acuerdo que eran más probablemente en busca de plantillas variadic, pero por el bien de ella, diferentes enfoques que se pueden tomar en C++ 03 :

el uso de un tipo de variante

uso de un recipiente de un tipo de variante. En este caso boost::variant no funcionará, ya que limita el número de tipos, pero se puede utilizar boost::any:

void foo(std::vector<boost::any> args); 

En comparación con las plantillas variadic, código de usuario será mucho más complicado, ya que en lugar de la escritura foo(a, b, c, d), lo harán tiene que crear manualmente el vector por adelantado. La sintaxis podría simplificarse mediante macros variadas (si el compilador las admite) y/o funciones de plantilla auxiliar para adaptar la sintaxis, pero esto puede convertirse fácilmente en un desastre.

La forma C (sin plantilla):

utilizar la notación de puntos suspensivos para escribir una función que toma un número indeterminado de argumentos (y tipos):

void foo(type x, ...) 

Este enfoque tiene muchas shortcommings. El primero es que no es seguro, el compilador no podrá detectar que los argumentos son el número o los tipos correctos, y es un comportamiento indefinido si alguno de los argumentos es un tipo no POD, lo que limita la usabilidad de cualquier tipo de a tipos de POD, lo que puede o no ser un factor limitante (siempre se puede pasar un puntero a su objeto no POD). En general, es más complejo de manejar y mucho más propenso a errores, por lo que debe evitarse.

no responder a la pregunta en absoluto

En muy pocos casos una única función debe ser capaz de tomar un número indeterminado de argumentos de tipos desconocidos. El registro y E/S pueden requerir esto, siendo printf dicho ejemplo. Pero eso se puede manejar en C++ mediante la sobrecarga del operador (en particular, operator<<) y el encadenamiento.En un comentario bind ha sugerido, por lo que sí, reenvío perfecto en el código genérico es uno de esos casos, bind, std::thread ...

Se piensa que esto es una buena respuesta para una entrevista, ya que podemos hablar de lo que la la necesidad real de la función es, y si existe alguna alternativa mejor. Se puede argumentar que si al final necesita un contenedor de tipo variante, puede abusar de la sobrecarga del operador para simplificar la sintaxis. Ejemplos de esto serían la biblioteca boost::assign, y en aquellas líneas que pueden crear un constructor argumento ayudante como en:

class args { 
public: 
    args() {} 
    operator std::vector<boost::any>&() { 
     return v; 
    } 
    template <typename T> 
    args& operator,(T x) { 
     boost::any a = x; 
     v.push_back(a); 
     return *this; 
    } 
private: 
    std::vector<boost::any> v; 
}; 
// usage: 
void foo(std::vector<boost::any> a) { 
    std::cout << "Received " << a.size() << " arguments" << std::endl; 
} 
int main() { 
    foo((args(), 1, 5.0, "a string", std::vector<int>(5,10))); 
} 

plantillas variadic

y por supuesto, la mejor opción que es un C++ 0x compilador que maneja plantillas variadic, que no requiere código de placa de caldera adicional, y hará que sea mucho más simple escribir el código de usuario (directamente como una llamada de función normal) y la implementación de la función, sea lo que sea. Como un ejemplo motivador, construyendo un vector<boost::any> con varios args:

typedef std::vector<boost::any> anyvector_t 

// Stop condition, adding nothing at the end 
void build_vector_impl(anyvector_t&) {} 

// Intermediate step, add a new argument to the vector and recurse: 
template <typename Head, typename... Tail> 
void build_vector_impl(anyvector_t& v, Head head, Tail... tail) { 
    v.push_back(boost::any(head)); 
    build_vector_impl(v, tail...); 
} 

// Syntactic sugar: make it return the vector: 
template <typename... Args> 
anyvector_t build_vector(Args... args) { 
    anyvector_t res; 
    build_vector_impl(res, args...); 
    return res; 
} 
// Test: 
int main() { 
    std::cout << "Number of args: " 
      << build_vector(1, 5, "Hi", std::vector<int>(5, 10)).size() 
      << std::endl; 
} 
+0

Visual Studio sí admite macros variadic. – Puppy

+0

@DeadMG: corregido, cuando trabajé con VS teníamos que admitir VS2003/05/08, y las macros variadas estaban prohibidas, probablemente debido a restricciones de 2003, ya que parecen estar disponibles en 2005. Gracias por la pista. –

+0

"Todavía tengo que encontrar una sola situación donde una sola función ..." std :: bind()? – ybungalobill

Cuestiones relacionadas