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

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

The above code compiles successfully due to type conversion, even though a char array is passed where a bool is expected.

Is it possible to cause this code to fail compilation? (C++03 solution preferred, since the compiler at my workplace is ancient.)

I have looked at the following related questions on StackOverflow, but they don't quite address this problem. Preventing implicit conversion in C++, Why does the compiler choose bool over string for implicit typecast of L""?

Community
  • 1
  • 1
Masked Man
  • 1
  • 7
  • 40
  • 80
  • 5
    You are **not** passing an `std::string`. You are passing a `const char[5]`. That decays to a `const char*`, which is converted to `bool`. – juanchopanza Aug 27 '13 at 11:21
  • @juanchopanza Yes, sorry I realised that after posting. I have updated the question now. – Masked Man Aug 27 '13 at 11:22

3 Answers3

13

You can declare a function that takes const char* and don't provide a definition:

void setBar(const char*);

This will make it fail at link time. You're still left will all other implicit conversion, though - from any pointer to bool, integral to bool, floats to bool...

Another option:

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

This way you'll get an error about it being private if you call it with anything else than bool.

jrok
  • 54,456
  • 9
  • 109
  • 141
10

One option would be to make setBar a template, and allow it only to work with 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
}

Alternatively, you can use SFINAE with std::enable_if to the same effect, although the compiler warning might be less easy to read:

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;
};
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
6

There is a common idiom that both avoids this issue and provides other advantages. Instead of using bool, you can create a custom type that more clearly describes the state that it represents.

The type bool represents only a generic value of true or false, while in actual usage you are overloading these states to mean something more specific. Here's an example using an enum to define a new type:

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
}

This still allows implicit conversion from any arithmetic or floating type. To avoid this, you can use C++11's enum class, or roll your own strongly-typed bool like this:

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

typedef Bool<struct BarTag> Bar;
const Bar Ok = { false };
const Bar FooBar = { true };
willj
  • 2,991
  • 12
  • 24