2016-09-20 13 views
7

W poniższym przykładzie, mój wynik jest „pojedynczy konstruktor param”, dwa razy. Moje pytanie jest dwojaka, po pierwsze, dlaczego jest konstruktor bool nazywane zamiast wersji zawierającej ciąg znaków, a po drugie, czy istnieje sposób, aby uzyskać wersji ciąg o nazwie bez rzucania rozmowę?wyraźny konstruktor niewłaściwego typu nazywane

Tak na marginesie, mój rzeczywisty kod nie nazwać konstruktorów z zakodowane ciągi ale z czymś takim:

static constexpr auto NONE = "NONE"; 
Foo aFoo(NONE); 

...

#include <iostream> 
#include <string> 

using namespace std; 


struct Foo { 

    explicit Foo(bool _isVisible = false): id(""), isVisible(_isVisible) {cout << "single param ctor" << endl;} 
    explicit Foo(const string _id, bool _isVisible = false): id(_id), isVisible(_isVisible) {cout << "multip param ctor" << endl;} 

    const string id; 
    bool isVisible; 
}; 

int main(int argc, const char * argv[]) { 

    shared_ptr<Foo> sharedFoo = make_shared<Foo>("hello"); 
    Foo regFoo("hi"); 
    return 0; 
} 

Odpowiedz

8

To ograniczenie C++ odziedziczone po starszym C++ i ostatecznie od C.

Twoje literały ciągów konwertują się na bool lepiej niż na "zdefiniowany przez użytkownika" typ std::string.

Zamiast odlewania w zaproszeniu, można dodać konstruktora, że ​​trwa const char*, ewentualnie posiadające ją przekazać do istniejącej konstruktora który zabierze std::string:

explicit Foo(const char* _id, bool _isVisible = false) 
    : Foo(std::string(_id), _isVisible) 
{}; 

Ten konstruktor będzie „dokładne dopasowanie” (lub dostatecznie blisko) i uniemożliwić kradzieży rozgłosu przez bool.

Albo, ponieważ C++ 14, zamiast stosowania szarego the std::string literal, np "hi"s, choć trzeba pamiętać, aby zrobić to w callsite który jest nieco „meh” dla ogólnego rozwiązania.

Nawiasem mówiąc, jeśli naprawdę chcesz zabrać parametr std::string _id przez wartość, nie rób go const; uniemożliwiasz jej przeniesienie i konieczność kopiowania w inicjatorze członkowskim. W zależności od tego, co dzieje się w typowy callsite dodaje może więcej sensu:

explicit Foo(
    string _id, 
    bool _isVisible = false 
) 
    : id(std::move(_id)) 
    , isVisible(_isVisible) 
{} 

Jeśli zwykle przechodzą w lwartością, chociaż, przynajmniej zaakceptować const std::string& aby zapobiec jednej kopii.

+0

Gdyby nie to krzywo nonsens niejawna * string-dosłowny * do * konwersja bool * o wyższym priorytecie jest przestarzała w C++? – WhiZTiM

+0

@WhiZTiM: 'nullptr' został wprowadzony, aby pomóc w pewnych okolicznościach, ale jeśli nie zmienimy typu literałów łańcuchowych lub nie usuniemy rozpadu z tablicą-nazwą, ani nie zmienimy sposobu działania wskaźników, utknęliśmy na tym. Wprowadzono więc "" foo ", aby dać nam ciągi literałów, które nie ulegają problemowi, ale jest to przydatne tylko wtedy, gdy jesteś już świadomy problemu! A jeśli tak, to i tak stosunkowo łatwo jest rozwiązać u źródła (jak pokazano powyżej). –

+0

@WhiZTiM: BTW słowo, którego szukasz, jest "przestarzałe". –

0

dlaczego jest konstruktor bool nazywane zamiast wersji zawierającej ciąg

Ponieważ "hi" jest wskaźnik do char tablica znaków, który jest przekształcany do wskaźnika do char (dzięki, lekkość Races na orbicie), więc liczba, którą przerobiłem na bool.

Istnieje sposób na uzyskanie wersji łańcucha bez wywoływania połączenia?

Nie bardzo elegancki, ale ... można przejść drugą logiczną param

Foo regFoo("hi", false); 

wykluczyć pierwszemu konstruktor

+1

'' cześć' 'jest tablicą znaków, ale rodzaj wyrażenia zepsuje się do wskaźnika na znak całkiem szybko –

+0

@LightnessRacesinOrbit - thanks; poprawiony – max66

+0

Lub uczynić go prawdziwym literałem std :: string w C++ 14: 'Foo regFoo (" hi "s);' –

Powiązane problemy