2015-01-29 21 views
5

Rozważmy:Pośrednio nazywając kopiowania konstruktora

struct Foo 
{ 
    Foo(std::string str) {} 
}; 

struct Bar 
{ 
    Bar(Foo f) {} 
}; 

int main(int argc, char* argv[]) 
{ 
    Foo f("test"); 

    Bar b1(f); 
    Bar b2(std::string("test")); 
    Bar b3("test"); 

    return 0; 
} 

To nie skompilować na deklaracji b3 ('nie można przekonwertować argumentu 1 z 'const char [5]' do 'Foo''). Co ma sens, ponieważ nie ma bezpośredniej metody konwersji const char na Foo. Istnieje jednak sposób na przekonwertowanie const char na std :: string, a następnie użycie go do skonstruowania Foo (co dzieje się w b1 i b2), i to jest to, czego chcę, ponieważ sprawia, że ​​API jest ładniejszy używać (bez konieczności tworzenia instancji Foo lub std :: string jawnie za każdym razem).

Moje pytanie brzmi: czy istnieje sposób, aby kompilator mógł domyślnie wywoływać konstruktor kopii Foo (std :: string)? Innymi słowy, czy istnieje sposób na sporządzenie deklaracji takiej jak praca b3, niech będzie taka sama jak b2, i bez deklarowania konstruktora const char * copy dla Foo? (ta ostatnia rzecz jest oczywista, ale mój prawdziwy kod nie jest oczywiście tak prosty jak ten i wolałbym nie dodawać konstruktorów const char * copy i obsługiwać wszystkich innych inicjalizacji w konstruktorach poprawnie i utrzymywać to w synchronizacja z konstruktorem kopii std :: string).

+6

Niejawna sekwencja konwersji może zawierać tylko jedną konwersję zdefiniowaną przez użytkownika. Pytasz o dwie ('const char *' do 'std :: string', następnie' std :: string' do 'Foo'). Jednym ze sposobów byłoby dodanie przeciążenia konstruktora 'Foo' przy użyciu' const char * '. –

+0

Nitpick: Żaden z konstruktorów, o którym wspomniałeś, nie jest konstruktorem * copy *, jest po prostu konstruktorem. – molbdnilo

+0

Dzięki, "niejawna sekwencja konwersji" to magiczny ciąg, którego szukałem. Rzeczywiście niemożliwe jest zrobienie tego, co chciałem, będę musiał użyć dodatkowego konstruktora. – Roel

Odpowiedz

1

co zmienia to:

struct Bar 
{ 
    Bar(Foo f) {} 
}; 

do

struct Bar 
{ 
    Bar(Foo f) {} 
    Bar(std::string f) {} 
}; 

wykorzystaniem polimorfizmu ...

+5

Polimorfizm (w zwykłym sensie OOP) ma niewiele wspólnego z tym, to po prostu przeciążenie. – Puppy

+0

Dzięki, ale to cierpi (w moim przypadku) z tych samych problemów z utrzymaniem, których starałem się uniknąć, nie mając konstruktora const char *. Wygląda na to, że będę musiał stworzyć dodatkowego konstruktora, który wykona tę pracę. – Roel

8

Jeśli C++ 11 jest do przyjęcia, można dodać przekazał mu uprawnienia konstruktora Foo który zajmuje const char* i po prostu dzwoni do innego konstruktora:

struct Foo 
{ 
    Foo(std::string str) {} 
    Foo(const char* str) : Foo(std::string(str)) {} 
}; 

Alternatywnie, można użyć C++ 14 na std::string dosłowny:

using namespace::std::string_literals; 
Bar b3("test"s); 

Można też emulować przekazał mu uprawnienia konstruktora poprzez oba konstruktorzy nazywają odrębną funkcję:

struct Foo 
{ 
    Foo(std::string str) { init(str); } 
    Foo(const char* str) { init(str); } 
    void init(std::string str) {} 
}; 

Minusem powyżej musisz uważnie przemyśleć wszystko, co robiłeś na listach inicjujących, które teraz musisz wykonać w treści: init.

+0

VS2013, bez konstruktorów delegujących ani std :: string literal :( – Roel

+0

@Roel Jak o emulowaniu delegujących konstruktorów, tak jak w mojej edycji? – TartanLlama

+1

Tak, właśnie to skończę robić, jak sądzę.Wadą (jak na pewno wiesz) jest to, że członkowie nie będą budowani na miejscu. Które nie będą zauważalne lub mierzalne w ogóle w moim kodzie pod względem szybkości, ale nadal wydaje się lekko icky;) – Roel

Powiązane problemy