2016-03-12 12 views
5

Questa è la seconda volta che commetto un grosso errore, creando una variabile di tipo bool anziché doppia. Ad esempio si consideriC++ la conversione implicita da doppio a bool è pericolosa

double f() 
{ 
    return true; 
} 
bool something() 
{ 
    return 0.0; 
} 
double g() 
{ 
    bool x = 0.0; // (1) 
    if (something()) { 
     x = f(); // where f() is a function returning a double 
    } 
    return x; 
} 

Credo che il compilatore mi dovrebbe dire che questo è male, ma il mio compilatore (g ++) non emette un piccolo avviso con -Wall ... e conduce ad un bug in seguito a test. C'è qualche opzione per gcc/g ++ per avere avvertenze (per esempio sulla linea 1, che è chiaramente male)?

+0

Hai provato '-pedantic-errors'? –

+0

Ho appena provato con: g ++ -Wall -Wextra -pedantic-errors -c dummy.cpp e non ho ancora avvertimenti. – Renaud

+0

MSVC 14 genera [C4800] (https://msdn.microsoft.com/en-us/library/b6801kcy.aspx) per questo. – wally

risposta

3

È possibile utilizzare uniforme inizializzazione per ottenere un errore:

bool x{0.0}; 

error: type 'double' cannot be narrowed to 'bool' in initializer list [-Wc++11-narrowing]

Esso può essere utilizzato anche su incarico: x = {f()};, e tornando return {x};.

+0

Non ho un avviso con bool x {0.0}; Sto usando mingw-w64 con gcc-5.2 – Renaud

+1

@ Renaud Per qualche ragione gcc non implementa questo comportamento: ti consiglio di usare clang se vuoi usare questa funzionalità. – emlai

+0

@Renaud: sembra bug in gcc. [Http://melpon.org/wandbox/permlink/ 5vqO6rRq8x8sNiix] (http://example.com) – Destructor

0

In C++, costringendo il coder a gestire i tipi, libera risorse per il runtime. Il prezzo tiene traccia dei tipi, ma il vantaggio è l'efficienza e la velocità.

È un piccolo prezzo da pagare, ma la ricompensa è la possibilità di fare di più, in meno tempo.

+0

"costringendo il coder a gestire i tipi da solo, liberando risorse per il runtime". C++ è tipizzato staticamente, ad esempio tutti i tipi vengono controllati in fase di compilazione, quindi non c'è nulla da liberare per i tipi di runtime. – emlai

1

Il compilatore di Visual C++ avvisa della conversione in bool, ma con un avviso di prestazione stupido. Di solito è un avvertimento indesiderato, ma sfortunatamente non può essere messo a tacere con un cast semplice. Il quasi-idioma per mettere a tacere è usare una doppia negazione, !!, bang-bang, ad es. return !!0.0.

Il tuo problema è il contrario, che tu vuoi un tale avvertimento o errore, ma comunque il quasi-idioma bang-bang può essere parte di una soluzione.

Con l'idea esemplificato qui sotto, è sufficiente scrivere Bool invece di bool in cui si desidera un valore booleano, e utilizzare il !! per garantire pulite bool valori, altrimenti si ottengono errori di compilazione.

La cosa bella di questo è che molto probabilmente è sufficiente effettuare una ricerca globale e sostituire il codice, sostituendo bool con Bool.

#ifdef CLEAN 
# define TO_BOOL !! 
#else 
# define TO_BOOL 
#endif 

#define STATIC_ASSERT(e) static_assert(e, #e) 

#include <type_traits> // std::is_same 
#include <utility>  // std::enable_if_t 

class Bool 
{ 
private: 
    bool value_; 

public: 
    operator bool() const { return value_; } 

    template< class T 
     , class Enabled_ = std::enable_if_t<std::is_same<T,bool>::value, void> 
     > 
    auto operator=(T const other) 
     -> Bool& 
    { value_ = other; return *this; } 

    Bool(): value_() {} 

    template< class T 
     , class Enabled_ = std::enable_if_t<std::is_same<T,bool>::value, void> 
     > 
    Bool(T const value) 
     : value_(value) 
    {} 
}; 

auto f() 
    -> double 
{ return 0.0; } 

auto something() 
    -> Bool 
{ return TO_BOOL 0.0; }       // ← Line 43 

auto g() 
    -> double 
{ 
    Bool x = TO_BOOL 0.0;      // ← Line 48 
    if (something()) { 
     x = TO_BOOL f(); // where f() is a function returning a double 
    } 
    return x; 
} 

auto main() -> int 
{ 
    Bool a, b, c; 
    return a && b || something(); 
} 

compilation di esempio con g ++:

 
c:\my\forums\so\105> g++ foo.cpp 
foo.cpp: In function 'Bool something()': 
foo.cpp:43:22: error: could not convert '0.0' from 'double' to 'Bool' 
    { return TO_BOOL 0.0; }       // ← Line 43 
        ^
foo.cpp: In function 'double g()': 
foo.cpp:48:25: error: conversion from 'double' to non-scalar type 'Bool' requested 
     Bool x = TO_BOOL 0.0;      // ← Line 48 
         ^
foo.cpp:50:13: error: no match for 'operator=' (operand types are 'Bool' and 'double') 
      x = TO_BOOL f(); // where f() is a function returning a double 
      ^
foo.cpp:23:14: note: candidate: template<class T, class Enabled_> Bool& Bool::operator=(T) 
     auto operator=(T const other) 
      ^
foo.cpp:23:14: note: template argument deduction/substitution failed: 
foo.cpp:12:11: note: candidate: Bool& Bool::operator=(const Bool&) 
    class Bool 
     ^
foo.cpp:12:11: note: no known conversion for argument 1 from 'double' to 'const Bool&' 
foo.cpp:12:11: note: candidate: Bool& Bool::operator=(Bool&&) 
foo.cpp:12:11: note: no known conversion for argument 1 from 'double' to 'Bool&&' 
foo.cpp: In function 'int main()': 
foo.cpp:58:18: warning: suggest parentheses around '&&' within '||' [-Wparentheses] 
     return a && b || something(); 
       ^

c:\my\forums\so\105> g++ foo.cpp -D CLEAN 
foo.cpp: In function 'int main()': 
foo.cpp:58:18: warning: suggest parentheses around '&&' within '||' [-Wparentheses] 
     return a && b || something(); 
       ^

c:\my\forums\so\105> g++ foo.cpp -D CLEAN -Wno-parentheses 

c:\my\forums\so\105> _ 

Se volete conversione implicita da Bool a qualche altro tipo di bool non devono essere considerati, semplicemente fanno anche che il responsabile della conversione di un modello selezionato, come il costruttore e operatore di assegnazione.

+0

A parte l'aspetto "performance", qual è la tua opinione sulla leggibilità? Anche se apparentemente non è l'intenzione dell'avviso VC, l'ho visto spesso indicando un codice che potrebbe essere reso molto più chiaro con un controllo esplicito. Ad esempio, 'double x = 0.0; bool b = (x! = 0.0); 'versus' double x = 0.0; bool b = x; '. –

2

Anche se non ho una risposta diretta (è stato richiesto un avviso del compilatore), ho un opaque typedef library contenente un tipo "inconvertibool" che funziona come e con bool, ma non con altri tipi come int o double.Dà a tempo di compilazione errori per i casi nel tuo esempio:

foo.cpp: In function 'double f()': 
foo.cpp:5:31: error: cannot convert 'inconvertibool {aka opaque::inconvertibool}' to 'double' in return 
    return inconvertibool(true); 
          ^
foo.cpp: In function 'inconvertibool something()': 
foo.cpp:9:12: error: could not convert '0.0' from 'double' to 'inconvertibool {aka opaque::inconvertibool}' 
    return 0.0; 
      ^
foo.cpp: In function 'double g()': 
foo.cpp:13:23: error: conversion from 'double' to non-scalar type 'inconvertibool {aka opaque::inconvertibool}' requested 
    inconvertibool x = 0.0; // (1) 
        ^
foo.cpp:15:9: error: no match for 'operator=' (operand types are 'inconvertibool {aka opaque::inconvertibool}' and 'double') 
     x = f(); // where f() is a function returning a double 
     ^

Naturalmente, questo sarebbe solo aiutare se si usa costantemente questo tipo, invece di bool, ma non corrispondono del tutto lo scenario, perché hai detto che significava "doppio", non "bool".

Problemi correlati