c++ - matriz - La inicialización de la lista anidada(vector de vectores de cadenas) falla
leer cadena de caracteres en c (3)
Este codigo
#include <vector>
#include <string>
#include <iostream>
class MyClass
{
public:
MyClass(const std::vector<std::vector<std::string>> & v)
{
std::cout << "Vector of string vectors size: " << v.size() << "/n";
for (size_t i = 0; i < v.size(); i++)
std::cout << "Vector #" << i << " has size " << v[i].size() << "/n";
}
};
int main()
{
MyClass({ { "a" } }); // <--- ok
MyClass({ { "a", "b" } }); // <--- PROBLEM
MyClass({ { std::string("a"), "b" } }); // <--- ok
MyClass({ { "a", "b", "c" } }); // <--- ok
MyClass({ { "a" },{ "c" } }); // <--- ok
MyClass({ { "a", "b" },{ "c", "d" } }); // <--- ok
}
produce esto (Visual Studio 2017):
Vector of string vectors size: 1
Vector #0 has size 1
Vector of string vectors size: 4
Vector #0 has size 97
Vector #1 has size 0
Vector #2 has size 0
Vector #3 has size 0
Vector of string vectors size: 1
Vector #0 has size 2
Vector of string vectors size: 1
Vector #0 has size 3
Vector of string vectors size: 2
Vector #0 has size 1
Vector #1 has size 1
Vector of string vectors size: 2
Vector #0 has size 2
Vector #1 has size 2
Por lo tanto, funciona bien en todos los casos, excepto en el caso en que tenemos un vector de un vector, que contiene dos cadenas. También funciona en el caso anterior si construimos explícitamente std :: string a partir de uno de los literales de cadena. Si ambos son solo literales de cadena simple, el compilador parece "confundirse" y construye un vector de 4 elementos, el primero de los cuales contiene 97 cadenas. Tenga en cuenta que 97 es el código de carácter de "a".
Supongo que mi pregunta es: ¿debería el compilador interpretar esta construcción problemática como es de esperar, o es este código incorrecto para inicializar una lista anidada como esta?
El vector interno en MyClass({ { "a", "b" } })
está creando mediante el constructor de rango:
template <class InputIterator>
vector (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());
Esto sucede porque { "a", "b" }
se interpreta como std::initializer_list<std::string>
sino como un par de punteros en bruto.
Entrar en el constructor ofensivo en el depurador revela que VC ++ ha elegido el vector<vector<int>>
constructor que toma dos iteradores (en este caso son caracteres const char*
s).
Es decir, trata la construcción como
std::vector<std::vector<std::string>> {"a", "b"}
Esto, por supuesto, conduce a un comportamiento indefinido ya que los dos punteros no pertenecen a la misma matriz.
Como nota al margen, g ++ compila ambos
std::vector<std::vector<std::string>> as{{"a", "b"}};
std::vector<std::vector<std::string>> bs{"a", "b"};
pero choca miserablemente en el segundo, mientras que el primero se comporta como se espera.
VC ++ compila la construcción variable de doble refuerzo de la forma que se esperaría, por lo que sospecho (espero) que haya un error en VC ++.
He encontrado una solución que ayuda a evitar este comportamiento indefinido con VC ++. Puedes definir un segundo constructor como este:
MyClass(const std::vector<std::vector<int>> &)
{
}
Luego, las líneas de código que darían el problema,
MyClass({ { "a", "b" } }); // <--- PROBLEM
no compilará más y le dará el error "la resolución de sobrecarga del constructor fue ambigua", que apunta al problema. Luego puede escribir el literal a std :: string para resolver el problema.