En C ++ 11, ¿cómo haría para escribir una función (o método) que tome una matriz std :: de tipo conocido pero de tamaño desconocido?
// made up example
void mulArray(std::array<int, ?>& arr, const int multiplier) {
for(auto& e : arr) {
e *= multiplier;
}
}
// lets imagine these being full of numbers
std::array<int, 17> arr1;
std::array<int, 6> arr2;
std::array<int, 95> arr3;
mulArray(arr1, 3);
mulArray(arr2, 5);
mulArray(arr3, 2);
Durante mi búsqueda, solo encontré sugerencias para usar plantillas, pero parecen desordenadas (definiciones de métodos en el encabezado) y excesivas para lo que estoy tratando de lograr.
¿Existe una forma sencilla de hacer que esto funcione, como lo haría con matrices simples de estilo C?
std::vector
.std::vector
como recomienda @TravisPessetto?Respuestas:
No. Realmente no puede hacer eso a menos que convierta su función en una plantilla de función (o use otro tipo de contenedor, como un
std::vector
, como se sugiere en los comentarios a la pregunta):He aquí un ejemplo en vivo .
fuente
template<typename C, typename M> void mulArray(C & arr, M multiplier) { /* same body */ }
El tamaño del
array
es parte del tipo , por lo que no puede hacer exactamente lo que quiere. Hay un par de alternativas.Se prefiere tomar un par de iteradores:
Alternativamente, use en
vector
lugar de matriz, que le permite almacenar el tamaño en tiempo de ejecución en lugar de como parte de su tipo:fuente
Lo intenté a continuación y funcionó para mí.
SALIDA:
1 2 3 4 5 6 7
2 4 6 8 10 12
1 1 1 1 1 1 1 1 1
3 6 9 12 15 18 21
10 20 30 40 50 60
2 2 2 2 2 2 2 2 2
fuente
template
.auto foo(auto bar) { return bar * 2; }
actualmente no es C ++ válido a pesar de que se compila en GCC7 con el indicador C ++ 17 configurado. De la lectura aquí , los parámetros de función declarados como auto son parte de Concepts TS que eventualmente debería ser parte de C ++ 20.EDITAR
C ++ 20 incluye tentativamente
std::span
https://en.cppreference.com/w/cpp/container/span
Respuesta original
Lo que desea es algo como
gsl::span
, que está disponible en la Biblioteca de soporte de pautas descrita en las Pautas principales de C ++:https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#SS-views
Puede encontrar una implementación de solo encabezado de código abierto del GSL aquí:
https://github.com/Microsoft/GSL
Con
gsl::span
, puedes hacer esto:El problema
std::array
es que su tamaño es parte de su tipo, por lo que tendría que usar una plantilla para implementar una función que tenga unstd::array
tamaño arbitrario.gsl::span
por otro lado, almacena su tamaño como información en tiempo de ejecución. Esto le permite utilizar una función que no sea de plantilla para aceptar una matriz de tamaño arbitrario. También aceptará otros contenedores contiguos:Bastante bien, ¿eh?
fuente
Absolutamente, hay una forma sencilla en C ++ 11 de escribir una función que toma una matriz std :: de tipo conocido, pero de tamaño desconocido.
Si no podemos pasar el tamaño de la matriz a la función, en su lugar, podemos pasar la dirección de memoria de donde comienza la matriz junto con una segunda dirección de donde termina la matriz. Más tarde, dentro de la función, podemos usar estas 2 direcciones de memoria para calcular el tamaño de la matriz.
Salida en consola: 10, 20, 2, 4, 8
fuente
Esto se puede hacer, pero se necesitan algunos pasos para hacerlo de manera limpia. Primero, escriba un
template class
que represente un rango de valores contiguos. Luego, reenvíe unatemplate
versión que sepa qué tan grandearray
es a laImpl
versión que toma este rango contiguo.Finalmente, implemente la
contig_range
versión. Tenga en cuenta quefor( int& x: range )
funciona paracontig_range
, porque implementébegin()
yend()
y los punteros son iteradores.(no probado, pero el diseño debería funcionar).
Luego, en su
.cpp
archivo:Esto tiene la desventaja de que el código que recorre el contenido de la matriz no sabe (en el momento de la compilación) qué tan grande es la matriz, lo que podría costar la optimización. Tiene la ventaja de que la implementación no tiene que estar en el encabezado.
Tenga cuidado al construir explícitamente a
contig_range
, ya que si lo pasa aset
, asumirá que losset
datos son contiguos, lo cual es falso, y realizarán un comportamiento indefinido en todas partes. Los únicos dosstd
contenedores en los que se garantiza que esto funcionará sonvector
yarray
(¡y las matrices de estilo C, como sucede!).deque
a pesar de ser el acceso aleatorio no es contiguo (peligrosamente, es contiguo en pequeños fragmentos),list
ni siquiera está cerca, y los contenedores asociativos (ordenados y desordenados) son igualmente no contiguos.Así que los tres constructores implementé donde
std::array
,std::vector
y al estilo de C matrices, que cubre básicamente las bases.Ejecución
[]
es fácil también, y entrefor()
y[]
que es más de lo que quiere unaarray
para, ¿verdad?fuente
template
función realmente corta sin casi detalles de implementación. LaImpl
función no es unatemplate
función, por lo que puede ocultar felizmente la implementación en el.cpp
archivo de su elección. Es un tipo de borrado de tipo realmente crudo, donde extraigo la capacidad de iterar sobre contenedores contiguos en una clase más simple, y luego pasar eso a través ... (aunquemultArrayImpl
toma atemplate
como argumento, no es atemplate
sí mismo).&*
desreferencias el iterador (que puede no ser un puntero), luego hace un puntero a la dirección. Para datos de memoria contiguos, el punterobegin
ay el puntero a uno-pasado-elend
también son iteradores de acceso aleatorio, y son del mismo tipo para cada rango contiguo sobre un tipoT
.