Ostatnio napotkałem problem podczas próby zaimplementowania hierarchii klas za pomocą doskonałych konstruktorów przekazywania. Rozważmy następujący przykład:Konflikt między idealnym konstruktorem przekazywania i konstruktorem kopiowania w hierarchii klas
struct TestBase {
template<typename T>
explicit TestBase(T&& t) : s(std::forward<T>(t)) {} // Compiler refers to this line in the error message
TestBase(const TestBase& other) : s(other.s) {}
std::string s;
};
struct Test : public TestBase {
template<typename T>
explicit Test(T&& t) : TestBase(std::forward<T>(t)) {}
Test(const Test& other) : TestBase(other) {}
};
Kiedy próbuję skompilować kod pojawia się następujący błąd:
Error 3 error C2664: 'std::basic_string<_Elem,_Traits,_Alloc>::basic_string(const std::basic_string<_Elem,_Traits,_Alloc> &)' : cannot convert parameter 1 from 'const Test' to 'const std::basic_string<_Elem,_Traits,_Alloc> &'
Moje zrozumienie jest, że kompilator traktuje doskonały konstruktor przekierowania jako lepszą matematyki niż z skopiować konstruktora. Zobacz na przykład Scott Meyers: Copying Constructors in C++11. W innych implementacjach bez hierarchii klas mogłem wyłączyć idealny konstruktor przekazujący z konstruktora kopiowania przez SFINAE. Zobacz na przykład Martinho Fernandes: Some pitfalls with forwarding constructors. Kiedy próbuję zastosować wymienione rozwiązanie do tego przykładu, nadal nie mogę skompilować się z tym samym komunikatem o błędzie.
Myślę, że jednym z możliwych rozwiązań byłoby uniknięcie perfekcyjnego przekazywania, pobranie parametrów według wartości w konstruktorach i przejście od nich do zmiennych klasy.
Moje pytanie brzmi, czy istnieją inne rozwiązania tego problemu, czy też w takim przypadku nie jest możliwe doskonałe przekazywanie?
Aktualizacja: Okazało się, że moje pytanie łatwo jest źle zrozumieć. Postaram się więc nieco wyjaśnić moje intencje i kontekst.
- Kod jest kompletny, jak podano w pytaniu. Nie są tworzone żadne inne obiekty ani funkcje. Wystąpił błąd podczas próby kompilacji wysłanego przykładu.
- Celem konstruowania idealnego konwertera przekazującego jest inicjalizacja członka, a , a nie posiadanie jakiegoś dodatkowego konstruktora kopii. Powodem jest tutaj zapisanie niektórych kopii obiektów podczas inicjowania elementów z obiektami tymczasowymi (zgodnie z propozycją w rozmowach Scotta Meyersa).
- Niestety, jak się okazało, idealny konstruktor przekazujący może kolidować z innymi przeciążonymi konstruktorami (w tym przykładzie z konstruktorami kopiowania) .
- Podobnie jak w odpowiedziach i komentarzach na to pytanie zasugerowano: Możliwe rozwiązania polegałyby na wprowadzeniu jawnych rzutów lub posiadaniu oddzielnych niesformowanych konstruktorów (tj. W odniesieniu do przykładu mającego dwa konstruktory o parametrach odpowiednio
const string&
istring&&
).
Możliwe, że napisałeś coś takiego jak 'std :: string (test)' 'zamiast std :: string (test.s) '? Pokaż nam wiersz 130 pliku main.cc – Andrey
Napisałeś 's (std :: forward (t))' zamiast 's (std :: forward (t) .s)'. –
avakar
Zwykle mieszanie przeciążeń i szablonów przesyłania dalej jest złym pomysłem. Zrobiłbym pojedynczy konstruktor z 'ciągu' i dodałbym szablon funkcji' make_test' * *. –