2013-08-27 14 views
8
struct Foo { 
    void setBar(bool bar_) { bar = bar_; } 
    bool bar; 
}; 

int main() { 
    Foo f; 
    f.setBar("true"); 
} 

Powyższy kod kompiluje się pomyślnie z powodu konwersji typu, mimo że tablica znaków jest przekazywana tam, gdzie oczekiwany jest kod bool.Jak zapobiec niejawnej konwersji z tablicy znaków do bool

Czy można spowodować, że ten kod nie powiedzie się w kompilacji? (Preferowane rozwiązanie C++ 03, ponieważ kompilator w moim miejscu pracy jest starożytny).

Przejrzałem następujące powiązane pytania dotyczące StackOverflow, ale nie rozwiązały one tego problemu. Preventing implicit conversion in C++, Why does the compiler choose bool over string for implicit typecast of L""?

+5

Jesteś ** nie ** przepuszczanie 'std :: string'. Przekazujesz 'const char [5]'. To rozpada się na "const char *", który jest konwertowany na 'bool'. – juanchopanza

+0

@juanchopanza Tak, przepraszam, zdałem sobie sprawę, że po wysłaniu. Zaktualizowałem teraz pytanie. –

Odpowiedz

9

Można zadeklarować funkcję, która pobiera const char* i nie zawiera definicji:

void setBar(const char*); 

To sprawi, że nie na czas połączenia. Nadal jesteś w lewo będą wszystkie inne niejawna konwersja, choć - z dowolnego wskaźnika do bool, integralną bool, pływaki do bool ...

Inna opcja:

struct Foo { 
    void setBar(bool bar_) {} 
private: 
    template<typename T> 
    void setBar(T bar) {} 
}; 

ten sposób dostaniesz błąd związany z tym, że jest prywatny, jeśli nazywasz go inaczej niż bool.

+1

co z 'private'? możemy tego użyć? –

+0

@Afriza, oczywiście, to po prostu nie powiedzie się z innym komunikatem o błędzie. – jrok

+1

Awaria czasu połączenia nie jest tak dobra, jak błąd podczas kompilacji. Stąd prywatne: void setBar (const void *); bez definicji. Ponadto zalecam, aby nie używać szablonu (wyobraź sobie, że masz klasę otoki wokół słowa kluczowego). –

7

Jednym rozwiązaniem byłoby, aby setBar szablonu i pozostawić tylko do pracy z bool:

#include <type_traits> 

struct Foo 
{ 
    template <typename T> 
    void setBar(T bar_) 
    { 
    static_assert(std::is_same<bool,T>::value, "not bool"); 
    bar = bar_;   
    } 
    bool bar; 
}; 

int main() { 
    Foo f; 
    f.setBar(true); // OK 
    f.setBar("true"); // Error 
    f.setBar(1);  // Error 
} 

Alternatywnie, można użyć SFINAE z std::enable_if do tego samego efektu, chociaż ostrzeżenie kompilatora może być mniejsza łatwe do odczytania:

struct Foo 
{ 
    template<class T , 
      class = typename std::enable_if<std::is_same<bool,T>::value>::type > 
    void setBar(T bar_) 
    { 
     bar = bar_; 
    } 
    bool bar; 
}; 
+1

To i możesz użyć BOOST_STATIC_ASSERT dla rozwiązania C++ 03. – jrok

+0

Wspomniałbym również o 'std :: enable_if'. – lapk

+1

@PetrBudnik Dobry pomysł, dodałem przykład. – juanchopanza

5

Istnieje wspólny język, który pozwala uniknąć tego problemu i zapewnia inne korzyści. Zamiast używać bool, możesz utworzyć niestandardowy typ, który lepiej opisuje stan, który reprezentuje.

Typ reprezentuje tylko ogólną wartość true lub false, podczas gdy w rzeczywistości używasz przeciążenia tych stanów, aby oznaczać coś bardziej konkretnego. Oto przykład przy użyciu enum zdefiniowanie nowego typu:

enum Bar { ok, foobar }; 

struct Foo { 
    void setBar(Bar bar_) { bar = bar_; } 
    Bar bar; 
}; 

int main() { 
    Foo f; 
    f.setBar(foobar); // ok 
    f.setBar("true"); // error 
} 

To nadal umożliwia niejawna konwersja z dowolnego arytmetycznych lub pływającej typu. Aby tego uniknąć, można użyć C++ 11 na enum class lub toczyć własną silnie wpisany bool tak:

template<class Tag> 
struct Bool { bool value; }; 

typedef Bool<struct BarTag> Bar; 
const Bar Ok = { false }; 
const Bar FooBar = { true }; 
Powiązane problemy