c++ c++11 variadic

C++ 11 variable número de argumentos, mismo tipo específico



c++ variadic templates (6)

La pregunta es simple, ¿cómo implementaría una función tomando un número variable de argumentos (como la plantilla variadic), sin embargo, cuando todos los argumentos tienen el mismo tipo, por ejemplo, int.

Estaba pensando en algo parecido a esto;

void func(int... Arguments)

Alternativamente, ¿no funciona una afirmación estática recursiva sobre los tipos de trabajo?


@Skeen ¿Qué tal esto?

template <typename T> void func_1(std::initializer_list<T>&& a) { // do something } template <typename... T> void func(T&&... a) { func_1({std::forward<T>(a)...}); } int main() { func(1, 2, 3); // func(1, 2, 3, 4.0); // OK doesn''t compile }


Aquí hay una versión que elimina la función del conjunto de sobrecarga, en lugar de dar un static_assert. Esto le permite proporcionar otras sobrecargas de la función que podrían usarse cuando los tipos no son todos iguales, en lugar de una static_assert fatal que no se puede evitar.

#include <type_traits> template<typename... T> struct all_same : std::false_type { }; template<> struct all_same<> : std::true_type { }; template<typename T> struct all_same<T> : std::true_type { }; template<typename T, typename... Ts> struct all_same<T, T, Ts...> : all_same<T, Ts...> { }; template<typename... T> typename std::enable_if<all_same<T...>::value, void>::type func(T...) { }

Si desea admitir el reenvío perfecto, es probable que desee reducir los tipos antes de verificarlos, de modo que la función acepte una combinación de argumentos de valor lvalue y rvalue siempre que tengan el mismo tipo:

template<typename... T> typename std::enable_if<all_same<typename std::decay<T>::type...>::value, void>::type func(T&&...) { }

Alternativamente, si tiene un rasgo de propósito general para probar la conjunción lógica, puede hacerlo usando std::is_same lugar de escribir su propio all_same :

template<typename T, typename... Ts> typename std::enable_if<and_<is_same<T, Ts>...>::value, void>::type func(T&&, Ts&&...) { }

Debido a que esto requiere al menos un argumento, también necesitaría otra sobrecarga para admitir el caso de cero argumentos:

void func() { }

El and_ helper puede definirse así:

template<typename...> struct and_; template<> struct and_<> : public std::true_type { }; template<typename B1> struct and_<B1> : public B1 { }; template<typename B1, typename B2> struct and_<B1, B2> : public std::conditional<B1::value, B2, B1>::type { }; template<typename B1, typename B2, typename B3, typename... Bn> struct and_<B1, B2, B3, Bn...> : public std::conditional<B1::value, and_<B2, B3, Bn...>, B1>::type { };


Creo que puedes hacer esto especificando un tipo concreto al eliminar tus argumentos del paquete de argumentos. Algo como:

class MyClass{}; class MyOtherClass{}; void func() { // do something } template< typename... Arguments > void func( MyClass arg, Arguments ... args ) { // do something with arg func( args... ); // do something more with arg } void main() { MyClass a, b, c; MyOtherClass d; int i; float f; func( a, b, c ); // compiles fine func( i, f, d ); // cannot convert }

En el caso genérico void func( MyClass arg, Arguments ... args ) se convertiría en void func( arg, Arguments ... args ) con una plantilla de tipo T.


Debido a que no creo que haya visto esta solución, podría escribir una función específica para cada tipo (en su caso, solo int ) y luego una función de reenvío que tome tipos de argumentos variables.

Escribe cada caso específico:

Luego para cada caso específico:

// only int in your case void func(int i){ std::cout << "int i = " << i << std::endl; }

Entonces su función de reenvío de esta manera:

template<typename Arg0, typename Arg1 typename ... Args> void func(Arg0 &&arg0, Arg1 &&arg1, Args &&... args){ func(std::forward<Arg0>(arg0)); func(std::forward<Arg1>(arg1), std::forward<Args>(args)...); }

Esto es bueno porque es expandible para cuando quiera aceptar quizás otro tipo también.

Utilizado de esta manera:

int main(){ func(1, 2, 3, 4); // works fine func(1.0f, 2.0f, 3.0f, 4.0f); // compile error, no func(float) }


Si no desea utilizar un initializer_list / vector basado en corsé y desea mantener los argumentos separados en forma de paquete de argumentos, la solución a continuación la comprueba en tiempo de compilación usando static_assert recursivo s:

#include<type_traits> template<typename T1, typename T2, typename... Error> struct is_same : std::false_type {}; template<typename T, typename... Checking> struct is_same<T, T, Checking...> : is_same<T, Checking...> {}; template<typename T> struct is_same<T,T> : std::true_type {}; template<typename... LeftMost> void func (LeftMost&&... args) { static_assert(is_same<typename std::decay<LeftMost>::type...>::value, "All types are not same as ''LeftMost''"); // ... } int main () { int var = 2; func(1,var,3,4,5); // ok func(1,2,3,4.0,5); // error due to `static_assert` failure }

En realidad, esta solución verificará todos los argumentos con respecto al primer argumento. Supongamos que fuera double entonces todo se verificará contra el double .


Una posible solución es hacer que el tipo de parámetro sea un contenedor que pueda inicializarse mediante una lista de inicializadores de corchetes, como std::initializer_list<int> o std::vector<int> . Por ejemplo :

#include <iostream> #include <initializer_list> void func(std::initializer_list<int> a_args) { for (auto i: a_args) std::cout << i << ''/n''; } int main() { func({4, 7}); func({4, 7, 12, 14}); }