I'd like to have a simple way of checking for an object to be valid. I thought of a simple conversion function, something like this:
operator bool() const { return is_valid; }
Checking for it to be valid would be very simple now
// is my object invalid?
if (!my_object) std::cerr << "my_object isn't valid" << std::endl;
Is this considered a good practise?
In C++03, you need to use the safe bool idiom to avoid evil things:
int x = my_object; // this works
In C++11 you can use an explicit conversion:
explicit operator bool() const
{
// verify if valid
return is_valid;
}
This way you need to be explicit about the conversion to bool, so you can no longer do crazy things by accident (in C++ you can always do crazy things on purpose):
int x = my_object; // does not compile because there's no explicit conversion
bool y = bool(my_object); // an explicit conversion does the trick
This still works as normal in places like if and while that require a boolean expression, because the condition of those statements is contextually converted to bool:
// this uses the explicit conversion "implicitly"
if (my_object)
{
...
}
This is documented in §4[conv]:
An expression e can be implicitly
converted to a type T if and only if
the declaration T t=e; is well-formed,
for some invented temporary variable t
(§8.5). Certain language constructs
require that an expression be
converted to a Boolean value. An
expression e appearing in such a
context is said to be contextually converted to bool and is well-formed
if and only if the declaration bool t(e); is well-formed, for some
invented temporary variable t (§8.5). The effect of either
implicit conversion is the same as performing the
declaration and initialization and then using the temporary
variable as the result of the conversion.
(What makes the difference is the use of bool t(e); instead of bool t = e;.)
The places were this contextual conversion to bool happens are:
the conditions of if, while, and for statements;
the operators of logical negation !, logical conjunction &&, and logical disjunction ||;
the conditional operator ?:;
the condition of static_assert;
the optional constant expression of the noexcept exception specifier;
No, a simple bool conversion operator is not, as you can now make evil comparisions between unrelated types. Generally, yes, a conversion function is a-okay. Just use the right one (safe-bool idiom). I can't explain it any better than the given links.
The original question was
Is this considered a good practise?
The issue with the safe bool conversion was very relevant in practice, but fortunately has now been addressed by the standards.
But the judgement if this approach is appropriate, is a question of design.
By introducing such a "validity check", effectively you are stating that your objects can be in an invalid state. That is, speaking in the terms of computer science, you added a new separate value to the value domain represented by your objects. A so called bottom value
The most prominent example for a value domain with that propery is the pointer. A pointer can refer to various memory locations, but it also can be NULL (invalid).
Thus we need do ask ourselves: does such a bottom value really reflect the nature of the things we want to model with our classes -- and -- do we really need to cover this aspect of the nature within our model?
Experience shows that bottom values tend to be error prone, easy to forget and generally more of a liability than an asset. If you're able to arrange your code in a way that your objects can not be invalid, your code gets simpler, easier to read, to understand and to maintain..
Related
#include <iostream>
typedef enum my_time {
day,
night
} my_time;
int main(){
// my_time t1 = 1; <-- will not compile
int t2 = night;
return 0;
}
How is it expected that I can assign an enum value to an int but not the other way in C++?
Of course this is all doable in C.
Implicit conversions, or conversions in general, are not mutual. Just because a type A can be converted to a type B does not imply that B can be converted to A.
Old enums (unscoped enums) can be converted to integer but the other way is not possible (implicitly). Thats just how it is defined. See here for details: https://en.cppreference.com/w/cpp/language/enum
Consider that roughly speaking enums are just named constants and for a function
void foo(my_time x);
It is most likely an error to pass an arbitrary int. However, a
void bar(int x);
can use an enum for special values of x while others are still allowed:
enum bar_parameter { NONE, ONE, MORE, EVEN_MORE, SOME_OTHER_NAME };
bar(NONE);
bar(SOME_OTHER_NAME);
bar(42);
This has been "fixed" in C++11 with scoped enums that don't implicitly convert either way.
You could cast to int. This expression makes an explicit conversion of the specified data type (int) and the given value (night).
int t2 = static_cast<int>(night)
Of course this is all doable in C
That doesn't mean that the minds behind C++ automatically consider it a desired behavior. Nor should they have such an attitude. C++ follows its own philosophy with regard to types. This is not the only aspect where a conscious decision was made to be more strongly typed than C. This valid C snippet is invalid in C++
void *vptr = NULL;
int *iptr = vptr; // No implicit conversion from void* to T* in C++
How is it expected that I can assign an enum value to an int but not the other way in C++?
It's the behavior because one side of the conversion is less error prone. Allowing an enumerator to become an integer isn't likely to break any assumptions the programmer has about an integer value.
An enumeration is a new type. Some of the enmueration's values are named. And for most cases of using an enumeration, we really do want to restrict ourselves to those named constants only.
Even if an enumeration can hold the integer value, it doesn't mean that value is one of the named constants. And that can easily violate the assumptions code has about its input.
// Precondition: e is one of the name values of Enum, under pain of UB
void frombulate_the_cpu(Enum e);
This function documents its precondition. A violation of the precondition can cause dire problems, that's what UB usually is. If an implicit conversion was possible everywhere in the program, it'd be that more likely that we violate the precondition unintentionally.
C++ is geared to catch problems at compile time whenever it can. And this is deemed problematic.
If a programmer needs to convert an integer an enumeration, they can still do it with a cast. Casts stand out in code-bases. They require a conscious decision to override a compiler's checks. And it's a good thing, because when something potentially unsafe is done, it should be with full awareness.
Cast the int when assigning . . .
my_time t1 = (my_time)1;
Probably been answered, but could not find it.
In c++ what does
if(a=b)
mean?
versus
if(a==b)
I just spent two hours debugging to find that
if(a=b)
compiles as
a=b
Why does compiler not flag
if(a=b)
as an error?
In c++ what does if(a=b) mean?
a=b is an assignment expression. If the type of a is primitive, or if the assignment operator is generated by the compiler, then the effect of such assignment is that the value of a is modified to match b. Result of the assignment will be lvalue referring to a.
If the operator is user defined, then it can technically have any behaviour, but it is conventional to conform to the expectations by doing similar modification and return of the left operand.
The returned value is converted to bool which affects whether the following statement is executed.
versus
if(a==b)
a==b is an equality comparison expression. Nothing is assigned. If the types are primitive, or if the comparison operator is generated by the compiler, then the result will be true when the operands are equal and otherwise false.
If the operator is user defined, then it can technically have any behaviour, but it is conventional to conform to the expectations by doing similar equality comparison.
Why does compiler not flag
if(a=b)
as an error?
Because it is a well-formed expression (fragment) as long as a is assignable with b.
if(a=b) is a conventional pattern to express the operation of setting value of a variable, and having conditional behaviour depending on the new value.
Some compilers do optionally "flag" it with a warning.
Note that if you would assign value int a = 1 and you make an if statement
if (a = 2)
{
std::cout << "Hello World!" << std::endl;
}
It still works, even though they are two different values, they will do the std::cout
However if you use a double equals sign == it will not.
The reason for this is if you use the standard double equals sign == you are asking the code if a is equivalent to 2, if it is 2. Obviously it's not, so it doesn't std::cout. But if you use an equals sign, you are changing the value a to 2, so it continues with the if statement.
And, to prove this, try taking away the int a = 1 from before the if statement and add an int before a in the if statement, it works.
In c++, bool is used to represent Boolean. that is it holds true or false. But in some case we can use bool to represent integers also.
what is the meaning of bool a=5; in c++?
"what is the meaning of bool a=5; in c++?"
It's actually equivalent to writing
bool a = (5 != 0);
"But in some case we can use bool to represent integers also."
Not really. The bool value only represents whether the integer used to initialize it was zero (-> false) or not (-> true).
The other way round (as mentioned in #Daniel Frey's comment) false will be converted back to an integer 0, and true will become 1.
So the original integer value's (or any other expression results like pointers, besides nullptr, or double values not exactly representing 0.0) will be lost.
Conclusion
As mentioned in LRiO's answer, it's not possible to store information other than false or true in a bool variable.
There are guaranteed rules of conversion though (citation from cppreference.com):
The safe bool problem
Until the introduction of explicit conversion functions in C++11, designing a class that should be usable in boolean contexts (e.g. if(obj) { ... }) presented a problem: given a user-defined conversion function, such as T::operator bool() const;, the implicit conversion sequence allowed one additional standard conversion sequence after that function call, which means the resultant bool could be converted to int, allowing such code as obj << 1; or int i = obj;.
One early solution for this can be seen in std::basic_ios, which defines operator! and operator void* (until C++11), so that the code such as if(std::cin) {...} compiles because void* is convertible to bool, but int n = std::cout; does not compile because void* is not convertible to int. This still allows nonsense code such as delete std::cout; to compile, and many pre-C++11 third party libraries were designed with a more elaborate solution, known as the Safe Bool idiom.
No, we can't.
It's just that there's an implicit conversion from integer to boolean.
Zero becomes false; anything else becomes true.
In fact this declaration
bool a=5;
is equivalent to
bool a=true;
except that in the first declaration 5 as it is not equal to zero is implicitly converted to true.
From the C++ Standard (4.12 Boolean conversions )
1 A prvalue of arithmetic, unscoped enumeration, pointer, or pointer
to member type can be converted to a prvalue of type bool. A zero
value, null pointer value, or null member pointer value is converted
to false; any other value is converted to true. For
direct-initialization (8.5), a prvalue of type std::nullptr_t can be
converted to a prvalue of type bool; the resulting value is false.
One more funny example
bool a = new int;
Of course it does not mean that we may use bool to represent pointers. Simply if the allocation is successfull then the returned poimter is not equal to zero and implicitly converted to true according to the quote I showed.
Take into account that till now some compilers have a bug and compile this code successfully
bool a = nullptr;
Though according to the same quote a valid declaration will look like
bool a( nullptr );
And, basically, I would opine that bool b=5 is "just plain wrong."
Both C and C++ have a comparatively weak system of typing. Nevertheless, I'm of the opinion that this should have produced a compile error ... and, if not, certainly a rejected code review, because it is (IMHO) more likely to be an unintentional mistake, than to be intention.
You should examine the code to determine whether b is or is not "really" being treated as a bool value. Barring some "magic bit-twiddling purpose" (which C/C++ programs sometimes are called-upon to do ...), this is probably a typo or a bug.
Vlad's response (below) shows what the standard says will happen if you do this, but I suggest that, "since it does not make human-sense to do this, it's probably an error and should be treated accordingly by the team.
A "(typecast)?" Maybe. A data-type mismatch such as this? Nyet.
I was reading code from the cpp-btree library of google (https://code.google.com/p/cpp-btree/) and I came accross that compile-time assert mechanism.
// A compile-time assertion.
template <bool>
struct CompileAssert {
};
#define COMPILE_ASSERT(expr, msg) \
typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
So I understand more or less what it does, if expr is evaluated to false by the compiler it will declare a new type msg that will be a CompileAssert < false > array of size -1 which will trigger a compilation error.
What I don't get is the bool(expr) part, what is this exactly? Some kind of call to the copy constructor of the class bool? (but it's a builtin type so I'm confused)
I though this would be a mechanism to raise a compilation error when expr is not a boolean but actually I managed to compile a short program whit that line
COMPILE_ASSERT("trash",error_compilation_assert);
It compiles just fine with gcc 3.4
So can anyone explain the bool(expr) part of the mechanism?
It's a type conversion. There are 3 main types of type conversions in C++:
Cast notation (C-style cast): (bool) expr
Functional notation (constructor-style cast): bool(expr)
Cast operators (C++-style cast); static_cast<bool>(expr)
Cast notation and functional notation are semantically equivalent (i.e. they both perform the strongest possible conversion, the C-cast), but the scope & precedence of the functional notation is clearer.
It is generally advised not to use them in C++ code and use the specific cast operators (const_cast, static_cast etc.) instead.
So in your code, it's just a way of forcing the value to type bool and enclosing it in parentheses at the same time, so that no operator priority issues arise.
bool(expr) casts expr into a bool.
The first parameter should be some kind of expression, such as a == b. Using a string literal here is useless.
bool(expr) is a function-style cast which converts the expression to a bool. Lots of things convert implicitly to bool, but I guess they wanted an explicit cast to make sure the result is a bool.
If you convert a pointer to a bool, it evaluates to false if it is a NULL pointer, or true otherwise. Your string literal "Trash" decays into a const char * to the first character. As this is not a null pointer, the expression evaluates to true.
bool(expr) tries to convert expr to bool either implicitly or using any user defined conversion operator.
In C++, you can instantiate built-in types with ctor syntax:
bool b1 = bool(true);
bool b2 = bool(b1);
The difference to:
bool b2 = b1;
is that the latter does an implicit conversion to bool. When such an implicit conversion isn't allowed (as in the template typedef), then bool(b1) makes it explicit by creating a temporary bool from b1 and the temporary doesn't have to be converted anymore; it's an actual bool type.
I am following the file boost/smart_ptr/detail/operator_bool.hpp and come across the following snippet of code that I do not understand
typedef T * this_type::*unspecified_bool_type;
operator unspecified_bool_type() const // never throws
{
return px == 0? 0: &this_type::px;
}
I write some test codes with XCode and &this_type::px always return 1. Why?
Can some C++ guru share your thoughts?
This is a little trick known as the Safe Bool Idiom.
The problem is that if you write a conversion operator to bool:
operator bool() const;
Then it can be used in some tricky situations, for example: 1 + sharedp with bool getting promoted to int... stupid eh ?
Therefore, the trick is to use a type that can be converted to bool but on which all other operations will provoke an error during compilation. The recommended way is to use a pointer-to-member in the class, and it is typedefed to an explicit name so that error messages are a bit more understandable.
With C++11, this trick is obsolete, because the explicit qualifier can be applied to conversion operator:
explicit operator bool() const { return px; }
much more pleasant, isn't it ?
&this_type::px is a trick used to obtain a boolean value equivalent to true.
Since boost does not use the bool type, but instead does not specifies what it is, it uses a pointer-to-member cast which always return the equivalent representation of true for an existing member (i.e. not nullptr or something cast from 0).
See 4.12:
A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to
member type can be converted to a prvalue of type bool.
The boolean conversion of pointer-to-members always happen in an integral context because there is no cast from pointer-to-member to an integer in C++.
It is not 1, but it is output by ostream as 1 (bool) if you havent switched on boolalpha flag. ostream has no special output operator for member pointers.