Soru İç içe geçmiş liste (dizelerin vektörlerinin vektörü) başlatma başarısız oluyor


Bu kod:

#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
}

çıktılar (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

Bu yüzden, iki vektör içeren bir vektörün bulunduğu durum haricinde, her durumda iyi çalışır. Açıkça std :: string'i dize değişmezlerinden birinden yapılandırırsak yukarıdaki durumda da çalışır. Her ikisi de sadece düz dize değişmezleri ise, derleyici "karışmış" gibi görünüyor ve ilkinde 97 dizeleri içeren 4 öğenin bir vektör oluşturur. 97'nin "a" karakter kodu olduğunu unutmayın.

Sanırım sorum şu ki, derleyici beklediğim gibi bu sorunlu yapıyı yorumlamalı mıdır, yoksa bu tür bir kod, böyle bir iç içe geçmiş listeyi başlatabilir mi?


18
2018-04-23 11:26


Menşei


FWIW, g ++ 6.4.0 "hata: aşırı yüklendi" diyor MyClass (<brace-kapalı başlatıcı listesi>) 'belirsiz'. - molbdnilo
Birkaç derleyiciyi denedim ... hepsi de belirsiz bir hata verdi - YesThatIsMyName
Dikkat edin void f(MyClass); f({{"a", "b"}}); belirsiz değil, MyClass... - Holt


Cevaplar:


İç vektör MyClass({ { "a", "b" } }) range constructor kullanarak oluşturuyor:

template <class InputIterator>
  vector (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());

Bu olur çünkü { "a", "b" } olarak yorumlanır std::initializer_list<std::string> ama bir çift ham işaretçi olarak.


17
2018-04-23 11:55





Hata ayıklayıcısında hata yapan kurucuya adım atmak VC ++’nun vector<vector<int>> iki yineleyici alır kurucu (onlar const char*Bu durumda s.
Yani, inşaat gibi davranır

std::vector<std::vector<std::string>> {"a", "b"}

Bu, elbette, iki işaretçi aynı diziye ait olmadığından, tanımlanmamış davranışlara yol açar.

Bir yan not olarak, g ++ her ikisini de derler

std::vector<std::vector<std::string>> as{{"a", "b"}};
std::vector<std::vector<std::string>> bs{"a", "b"};

ama eskiden beklendiği gibi davranır, ama ikincisine perişan olur.

VC ++, çift köşeli değişken yapısını beklediğiniz şekilde derler, dolayısıyla VC ++ 'da bir hata olduğunu tahmin ediyorum (umarım).


8
2018-04-23 11:51



Bu VC'nin haklı olduğu nadir bir durumdur. UB çağrıldığından, VC, VC2020'nin tam C ++ 20 uyumlu kaynak kodunu verebilir ve yine de doğru olabilir. - YSC
@YSC Davranış UB'dir, ancak derleyici clang ve g ++ gibi yapıcılar için belirsiz bir çağrı yapmamalı mıdır? - Holt
Cevap için teşekkürler. Bunun gibi bir vektör oluşturursam std :: vector <std :: vector <std :: string >> v2 ({{"a", "b"}}); (cevabınızdaki gibi) iyi çalışıyor, iki dizgisi olan bir vektörü içeren bir vektör elde edersiniz. Sorun, yalnızca bu başlatma listesi başka bir sınıf oluşturmak için kullanıldığında ortaya çıkar. - Urmas Rahu
@Holt Hayır o  meli  değil. - YSC
@YSC Katılıyorum, ama UB dahil olmalı ve aşırı yük çözünürlüğü yapılıncaya kadar UB kodu kullanılmıyor ve UB kurucusunu seçtiniz (aksi halde, çoğu C ++ kodu UB olacaktır). Benim için, 1) MSVC'nin doğru olup olmadığı (yani, UB kurucusunun daha iyi bir eşleşmesi), bu durumda kodun UB, 2) clang ve g ++ 'nın doğru olması, çağrının belirsiz olup olmadığı, 3) ve ilk kurucu çağrılmalıdır. - Holt


VC ++ ile bu tanımlanmamış davranıştan kaçınmaya yardımcı olan bir geçici çözüm buldum. Bunun gibi ikinci bir kurucu tanımlayabilirsiniz:

MyClass(const std::vector<std::vector<int>> &)
{
}

Ardından problemi verecek kod satırları,

MyClass({ { "a", "b" } }); // <--- PROBLEM

Artık derlemeyecek ve soruna işaret ederek, "kurucu aşırı yükleme çözünürlüğü belirsiz" hatası verecektir. Daha sonra, sorunu çözmek için literal'i std :: string'e yazabilirsiniz.


0
2018-04-25 07:03