I was just made aware of a bug I introduced, the thing that surprised me is that it compiled, is it legal to switch on a constant?
Visual Studio 8 and Comeau both accept it (with no warnings).
switch(42) { // simplified version, this wasn't a literal in real life
case 1:
std::cout << "This is of course, imposible" << std::endl;
}
It's not impossible that switching on a constant makes sense. Consider:
void f( const int x ) {
switch( x ) {
...
}
}
Switching on a literal constant would rarely make sense, however. But it is legal.
Edit: Thinking about it, there is case where switching on a literal makes
perfect sense:
int main() {
switch( CONFIG ) {
...
}
}
where the program was compiled with:
g++ -DCONFIG=42 foo.cpp
Not everything that makes sense to the compiler makes sense!
The following will also compile but makes no sense:
if (false)
{
std::cout << "This is of course, imposible" << std::endl;
}
It's up to us as developers to spot these.
One good reason for this being legal is that the compiler might well be able to resolve the value at compile time, depending on what stage of development you're at.
E.g. you might use something like this for debugging stuff:
int glyphIndex;
...
#if CHECK_INVALID_GLYPH
glyphIndex = -1;
#endif
switch (glyphIndex)
...
The compiler knows for certain that glyphIndex is -1 here, so it's as good as a constant. Alternatively, you might code it like this:
#if CHECK_INVALID_GLYPH
const int glyphIndex = -1;
#else
int glyphIndex = GetGlyph();
#endif
You wouldn't really want to have to change the body of your switch statement just so you could make little changes like this, and the compiler is perfectly capable of rationalising the code to eliminate the parts that will never be executed anyway.
Yes, it's perfectly legal to switch on any integer expression. It's the same as switching on an integer value returned by a function - a construct used quite often.
Yes, but why you'd want to (unless debugging) is another matter.
It's similar to if (0) or while (true).
Yes, it's legal.
Related
Currently, I write
assert(false);
at places that my code is never supposed to reach. One example, in a very C-ish style, is:
int findzero( int length, int * array ) {
for( int i = 0; i < length; i++ )
if( array[i] == 0 )
return i;
assert(false);
}
My compiler recognizes that the program finishes once assert(false) has been reached. However, whenever I compile with -DNDEBUG for performance reasons, the last assertion vanishes and the compiler warns that the execution finishes the function without a return statement.
What are better alternatives of finishing off a program if a supposedly unreachable part of the code has been reached? The solution should
be recognized by the compiler and not produce warnings (like the ones above or others)
perhaps even allow for a custom error message.
I am explicitly interested in solutions no matter whether it's modern C++ or like 90s C.
Replacing your assert(false) is exactly what "unreachable" built-ins are for.
They are a semantic equivalent to your use of assert(false). In fact, VS's is spelt very similarly.
GCC/Clang/Intel:
__builtin_unreachable()
MSVS:
__assume(false)
These have effect regardless of NDEBUG (unlike assert) or optimisation levels.
Your compiler, particularly with the above built-ins but also possibly with your assert(false), nods its head in understanding that you're promising that part of the function will never be reached. It can use this to perform some optimisations on certain code paths, and it will silence warnings about missing returns because you've already promised that it was deliberate.
The trade-off is that the statement itself has undefined behaviour (much like going forth and flowing off the end of the function was already). In some situations, you may instead wish to consider throwing an exception (or returning some "error code" value instead), or calling std::abort() (in C++) if you want to just terminate the program.
There's a proposal (P0627R0), to add this to C++ as a standard attribute.
From the GCC docs on Builtins:
If control flow reaches the point of the __builtin_unreachable, the program is undefined. It is useful in situations where the compiler cannot deduce the unreachability of the code. [..]
I like to use
assert(!"This should never happen.");
...which can also be used with a condition, as in
assert(!vector.empty() || !"Cannot take element from empty container." );
What's nice about this is that the string shows up in the error message in case an assertion does not hold.
As a fully portable solution, consider this:
[[ noreturn ]] void unreachable(std::string_view msg = "<No Message>") {
std::cerr << "Unreachable code reached. Message: " << msg << std::endl;
std::abort();
}
The message part is, of course, optional.
Looks like std::unreachable() made it to C++23:
https://en.cppreference.com/w/cpp/utility/unreachable
I use a custom assert that turns into __builtin_unreachable() or *(char*)0=0 when NDEBUG is on (I also use an enum variable instead of a macro so that I can easily set NDEBUG per scope).
In pseudocode, it's something like:
#define my_assert(X) do{ \
if(!(X)){ \
if (my_ndebug) MY_UNREACHABLE(); \
else my_assert_fail(__FILE__,__LINE__,#X); \
} \
}while(0)
The __builtin_unreachable() should eliminate the warning and help with optimization at the same time, but in debug mode, it's better to have an assert or an abort(); there so you get a reliable panic. (__builtin_unreachable() just gives you undefined behavior when reached).
I recommend C++ Core Gudelines's Expects and Ensures. They can be configured to abort (default), throw, or do nothing on violation.
To suppress compiler warnings on unreachable branches you can also use GSL_ASSUME.
#include <gsl/gsl>
int findzero( int length, int * array ) {
Expects(length >= 0);
Expects(array != nullptr);
for( int i = 0; i < length; i++ )
if( array[i] == 0 )
return i;
Expects(false);
// or
// GSL_ASSUME(false);
}
assert is meant for scenarios that are ACTUALLY supposed to be impossible to happen during execution. It is useful in debugging to point out "Hey, turns out what you thought to be impossible is, in fact, not impossible." It looks like what you should be doing in the given example is expressing the function's failure, perhaps by returning -1 as that would not be a valid index. In some instances, it might be useful to set errno to clarify the exact nature of an error. Then, with this information, the calling function can decide how to handle such error.
Depending on how critical this error is to the rest of the application, you might try to recover from it, or you might just log the error and call exit to put it out of its misery.
I believe the reason you are getting the errors is because assertions are generally used for debugging on your own code. When these functions are run in release, exceptions should be used instead with an exit by std::abort() to indicate abnormal program termination.
If you still want to use asserts, there is an answer about defining a custom one by PSkocik, as well as a link here where someone proposes the use of custom asserts and how to enable them in cmake here as well.
One rule that is sometimes found in style-guides is
"Never return from the middle of a function"
All functions should have a single return, at the end of the function.
Following this rule, your code would look like:
int findzero( int length, int * array ) {
int i;
for( i = 0; i < length; i++ )
{
if( array[i] == 0 )
break; // Break the loop now that i is the correct value
}
assert(i < length); // Assert that a valid index was found.
return i; // Return the value found, or "length" if not found!
}
I've seen if(true) used a bunch of times.
int a = 10;
if(true){
int b = 20;
}
int c = 15;
I don't understand what the point of putting if(true) there. Does it always evaluate to true, meaning it always executes? It's not part of a function. It's just there. Does it have to do with memory allocation?
This is equivalent to :
{
int b = 20;
}
maybe someone was using if (false) then switched to if (true). if(false) makes actually sense because you are removing some code - it should not get into compiled exe, but it gets compiled by compiler - and checked for any errors.
If one is fiddling with the code, it's very easy to turn
if (true) {
// block of code
}
into
if (false) {
// block of code
}
so this is a useful construct if you often need to turn a block of code on/off. It could also be a placeholder for future changes, where the boolean value is replaced with a (template) parameter or global constant or somesuch. (or a holdover from a former change that did the reverse)
I like to refactor complicated conditionals like this:
if (foo(blah) || (bar(param1, param2, param3) && !obj.longMethodName())) ...
into this:
bool foo_true = foo(blah);
bool bar_true = bar(param1, param2, param3);
bool long_true = obj.longMethodName();
if (foo_true || (bar_true && !long_true)) ...
I think this makes the code a lot easier to understand, and it helps with debugging because you can see the intermediate results used to compute the final condition.
But: in the original code, due to short circuiting, bar will only be evaluated if foo returns true, and longMethodName only if bar returns true.
Assume that functions are "pure", methods are const, and the compiler can see the function bodies of everything. Are C++ optimizers allowed to defer evaluating my intermediate values until they are needed?
Of course. Provided the compiler can see enough to determine
that foo, bar and obj.longMethodName() don't have any
other impact on the observable behavior of your code.
Whether any compilers do is another question. I rather doubt
it; it would require some very special logic, which isn't in the
usual list of optimization techniques, for something that
practically nobody does. (I suspect that most programmers would
find the original version, formatted correctly, to be more
readable than the one with a lot of extra variables.)
EDIT:
I wonder if it's worth pointing out that the compiler is allowed
to call all three functions even if the if is written:
if ( foo( blah ) || (bar( p1, p2, p3 ) && ! obj.lMN() ) )
(Although I can't imagine one that would.) The standard makes
no requirements with regards to which functions are called when;
it only requires that the observable behavior be the same (same
values and in the same order---no guarantees with regards to
time) "as if" the formal semantics of the program were followed.
And the only things that count as observable behavior is IO
(in some form) and accesses to volatile objects.
No. You compiler is not allowed to make the optimisation because it can not determine wether you have meant the short circuit or wether you want to have a potential side effect of evaluating bar no matter what.
No. C++ has no concept of a pure method with no side effects, so there really isn't a way to optimize that.
The problem here is that foo and bar could be implemented in another compilation unit and C++ does not have the concept of function purity. This means that foo and bar might have side effects (changes to screen or global variables) and therefore must be evaluated in order for you to get expected behaviour.
Interestingly enough, with GCC, functions can be declared with the pure attribute. This tells the compiler that the function does not have any side effects. And therefore can be called lazily. See here for more details.
I am not sure if the assignment would be counted as a side-effect already. To say the least it is probably hard to determine if it is safe to move the actual call.
But, I would like to point out that in c++11 it is possible to achieve what OP pursues with nearly exact same syntax that OP uses in examples utilizing std::bind.
It is just that foo_true, would not be defined as
bool foo_true = foo(blah);
but rather
auto foo_true = std::bind(foo, blah).
The if could be then checked as if( foo_true() || bar_true() ).
Whether it is cleaner or not, is up to personal matter IMO. But I believe it behaves as both wanted, and expected. Full code:
#include <iostream>
#include <functional>
using namespace std;
bool foo(int blah){
cout << "blah: " << blah << '\n';
return blah;
}
bool bar(bool negate_me){
cout << "negate_me: " << negate_me << '\n';
return !negate_me;
}
int main() {
bool test = true;
int param = 42;
auto foo_true = std::bind(foo, test);
auto bar_true = std::bind(bar, param);
if (foo_true() || bar_true() ) cout << "TEST\n";
return 0;
}
Output:
blah: 1
TEST
bar wasn't called. Change test to false and it will be.
I have the following situation: there's a huge set of templates like std::vector that will call memmove() to move parts of array. Sometimes they will want to "move" parts of length zero - for example, if the array tail is removed (like std::vector::erase()), they will want to move the remainder of the array which will happen to have length zero and that zero will be known at compile time (I saw the disassembly - the compiler is aware) yet the compiler will still emit a memmove() call.
So basically I could have a wrapper:
inline void callMemmove( void* dest, const void* source, size_t count )
{
if( count > 0 ) {
memmove( dest, source, count );
}
}
but this would introduce an extra runtime check in cases count is not known in compile time that I don't want.
Is it somehow possible to use __assume hint to indicate to the compiler that if it knows for sure that count is zero it should eliminate the memmove()?
The point of the __assume is to tell the compiler to skip portions of code when optimizing. In the link you provided the example is given with the default clause of the switch construct - there the hint tells the compiler that the clause will never be reached even though theoretically it could. You're telling the optimizer, basically, "Hey, I know better, throw this code away".
For default you can't not write it in (unless you cover the whole range in cases, which is sometimes problematic) because it would cause compilation error. So you need the hint to optimize the code you know that is unneeded out.
In your case - the code can be reached, but not always, so the __assume hint won't help you much. You have to check if the count is really 0. Unless you're sure it can never be anything but 0, then just don't write it in.
This solution uses a trick described in C++ compile-time constant detection - the trick uses the fact compile time integer zero can be converted to a pointer, and this can be used together with overloading to check for the "compile time known" property.
struct chkconst {
struct Small {char a;};
struct Big: Small {char b;};
struct Temp { Temp( int x ) {} };
static Small chk2( void* ) { return Small(); }
static Big chk2( Temp ) { return Big(); }
};
#define is_const_0(X) (sizeof(chkconst::chk2(X))<sizeof(chkconst::Big))
#define is_const(X) is_const_0( int(X)-int(X) )
#define memmove_smart(dst,src,n) do { \
if (is_const(n)) {if (n>0) memmove(dst,src,n);} \
else memmove(dst,src,n); \
} while (false)
Or, in your case, as you want to check for zero only anyway, one could use is_const_0 directly for maximum simplicity and portability:
#define memmove_smart(dst,src,n) if (is_const_0(n)) {} else memmove(dst,src,n)
Note: the code here used a version of is_const simpler than in the linked question. This is because Visual Studio is more standard conformant than GCC in this case. If targeting gcc, you could use following is_const variant (adapted to handle all possible integral values, including negative and INT_MAX):
#define is_const_0(X) (sizeof(chkconst::chk2(X))<sizeof(chkconst::Big))
#define is_const_pos(X) is_const_0( int(X)^(int(X)&INT_MAX) )
#define is_const(X) (is_const_pos(X)|is_const_pos(-int(X))|is_const_pos(-(int(X)+1)))
I think that you misunderstood the meaning of __assume. It does not tell the compiler to change its behavior when it knows what the values are, but rather it tells it what the values will be when it cannot infer it by itself.
In your case, if you told it to __assume that count > 0 it will skip the test, as you already told it that the result will always be true, it will remove the condition and will call memmove always, which is exactly what you want to avoid.
I don't know the intrinsics of VS, but in GCC there is a likely/unlikely intrinsic (__builtin_expect((x),1)) that can be used to hint the compiler as to which is the most probable outcome of the test. that will not remove the test, but will layout code so that the most probable (as in by your definition) branch is more efficient (will not branch).
If its possible to rename the memmove, I think something like this
would do - http://codepad.org/s974Fp9k
struct Temp {
int x;
Temp( int y ) { x=y; }
operator int() { return x; };
};
void memmove1( void* dest, const void* source, void* count ) {
printf( "void\n" );
}
void memmove1( void* dest, const void* source, Temp count ) {
memmove( dest, source, count );
printf( "temp\n" );
}
int main( void ) {
int a,b;
memmove1( &a,&b, sizeof(a) );
memmove1( &a,&b, sizeof(a)-4 );
}
I think the same is probably possible without the class - have to look at conversion rules
to confirm it.
Also it should be possible to overload the original memmove(), eg. by passing an
object (like Temp(sizeof(a)) as 3rd argument.
Not sure which way would be more convenient.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Inadvertent use of = instead of ==
C++ compilers let you know via warnings that you wrote,
if( a = b ) { //...
And that it might be a mistake that you certainly wanted to write:
if( a == b ) { //...
But is there a case where the warning should be ignored, because it's a good way to use this "feature"?
I don't see any code clarity reason possible, so is there a case where it’s useful?
Two possible reasons:
Assign & Check
The = operator (when not overriden) normally returns the value that it assigned. This is to allow statements such as a=b=c=3. In the context of your question, it also allows you to do something like this:
bool global;//a global variable
//a function
int foo(bool x){
//assign the value of x to global
//if x is equal to true, return 4
if (global=x)
return 4;
//otherwise return 3
return 3;
}
...which is equivalent to but shorter than:
bool global;//a global variable
//a function
int foo(bool x){
//assign the value of x to global
global=x;
//if x is equal to true, return 4
if (global==true)
return 4;
//otherwise return 3
return 3;
}
Also, it should be noted (as stated by Billy ONeal in a comment below) that this can also work when the left-hand argument of the = operator is actually a class with a conversion operator specified for a type which can be coerced (implicitly converted) to a bool. In other words, (a=b) will evaulate to true or false if a is of a type which can be coerced to a boolean value.
So the following is a similar situation to the above, except the left-hand argument to = is an object and not a bool:
#include <iostream>
using namespace std;
class Foo {
public:
operator bool (){ return true; }
Foo(){}
};
int main(){
Foo a;
Foo b;
if (a=b)
cout<<"true";
else
cout<<"false";
}
//output: true
Note: At the time of this writing, the code formatting above is bugged. My code (check the source) actually features proper indenting, shift operators and line spacing. The <'s are supposed to be <'s, and there aren't supposed to be enourmous gaps between each line.
Overridden = operator
Since C++ allows the overriding of operators, sometimes = will be overriden to do something other than what it does with primitive types. In these cases, the performing the = operation on an object could return a boolean (if that's how the = operator was overridden for that object type).
So the following code would perform the = operation on a with b as an argument. Then it would conditionally execute some code depending on the return value of that operation:
if (a=b){
//execute some code
}
Here, a would have to be an object and b would be of the correct type as defined by the overriding of the = operator for objects of a's type. To learn more about operator overriding, see this wikipedia article which includes C++ examples: Wikipedia article on operator overriding
while ( (line = readNextLine()) != EOF) {
processLine();
}
You could use to test if a function returned any error:
if (error_no = some_function(...)) {
// Handle error
}
Assuming that some_function returns the error code in case of an error. Or zero otherwise.
This is a consequence of basic feature of the C language:
The value of an assignment operation is the assigned value itself.
The fact that you can use that "return value" as the condition of an if() statement is incidental.
By the way, this is the same trick that allows this crazy conciseness:
void strcpy(char *s, char *t)
{
while( *s++ = *t++ );
}
Of course, the while exits when the nullchar in t is reached, but at the same time it is copied to the destination s string.
Whether it is a good idea, usually not, as it reduce code readability and is prone to errors.
Although the construct is perfectly legal syntax and your intent may truly be as shown below, don't leave the "!= 0" part out.
if( (a = b) != 0 ) {
...
}
The person looking at the code 6 months, 1 year, 5 years from now, at first glance, is simply going to believe the code contains a "classic bug" written by a junior programmer and will try to "fix" it. The construct above clearly indicates your intent and will be optimized out by the compiler. This would be especially embarrassing if you are that person.
Your other option is to heavily load it with comments. But the above is self-documenting code, which is better.
Lastly, my preference is to do this:
a = b;
if( a != 0 ) {
...
}
This is about a clear as the code can get. If there is a performance hit, it is virtually zero.
A common example where it is useful might be:
do {
...
} while (current = current->next);
I know that with this syntax you can avoid putting an extra line in your code, but I think it takes away some readability from the code.
This syntax is very useful for things like the one suggested by Steven Schlansker, but using it directly as a condition isn't a good idea.
This isn't actually a deliberate feature of C, but a consequence of two other features:
Assignment returns the assigned value
This is useful for performing multiple assignments, like a = b = 0, or loops like while ((n = getchar()) != EOF).
Numbers and pointers have truth values
C originally didn't have a bool type until the 1999 standard, so it used int to represent Boolean values. Backwards compatibility requires C and C++ to allow non-bool expressions in if, while, and for.
So, if a = b has a value and if is lenient about what values it accepts, then if (a = b) works. But I'd recommend using if ((a = b) != 0) instead to discourage anyone from "fixing" it.
You should explicitly write the checking statement in a better coding manner, avoiding the assign & check approach. Example:
if ((fp = fopen("filename.txt", "wt")) != NULL) {
// Do something with fp
}
void some( int b ) {
int a = 0;
if( a = b ) {
// or do something with a
// knowing that is not 0
}
// b remains the same
}
But is there a case where the warning
should be ignored because it's a good
way to use this "feature"? I don't see
any code clarity reason possible so is
there a case where its useful?
The warning can be suppressed by placing an extra parentheses around the assignment. That sort of clarifies the programmer's intent. Common cases I've seen that would match the (a = b) case directly would be something like:
if ( (a = expression_with_zero_for_failure) )
{
// do something with 'a' to avoid having to reevaluate
// 'expression_with_zero_for_failure' (might be a function call, e.g.)
}
else if ( (a = expression2_with_zero_for_failure) )
{
// do something with 'a' to avoid having to reevaluate
// 'expression2_with_zero_for_failure'
}
// etc.
As to whether writing this kind of code is useful enough to justify the common mistakes that beginners (and sometimes even professionals in their worst moments) encounter when using C++, it's difficult to say. It's a legacy inherited from C and Stroustrup and others contributing to the design of C++ might have gone a completely different, safer route had they not tried to make C++ backwards compatible with C as much as possible.
Personally I think it's not worth it. I work in a team and I've encountered this bug several times before. I would have been in favor of disallowing it (requiring parentheses or some other explicit syntax at least or else it's considered a build error) in exchange for lifting the burden of ever encountering these bugs.
while( (l = getline()) != EOF){
printf("%s\n", l);
}
This is of course the simplest example, and there are lots of times when this is useful. The primary thing to remember is that (a = true) returns true, just as (a = false) returns false.
Preamble
Note that this answer is about C++ (I started writing this answer before the tag "C" was added).
Still, after reading Jens Gustedt's comment, I realized it was not the first time I wrote this kind of answer. Truth is, this question is a duplicate of another, to which I gave the following answer:
Inadvertent use of = instead of ==
So, I'll shamelessly quote myself here to add an important information: if is not about comparison. It's about evaluation.
This difference is very important, because it means anything can be inside the parentheses of a if as long as it can be evaluated to a Boolean. And this is a good thing.
Now, limiting the language by forbidding =, where all other operators are authorized, is a dangerous exception for the language, an exception whose use would be far from certain, and whose drawbacks would be numerous indeed.
For those who are uneasy with the = typo, then there are solutions (see Alternatives below...).
About the valid uses of if(i = 0) [Quoted from myself]
The problem is that you're taking the problem upside down. The "if" notation is not about comparing two values like in some other languages.
The C/C++ if instruction waits for any expression that will evaluate to either a Boolean, or a null/non-null value. This expression can include two values comparison, and/or can be much more complex.
For example, you can have:
if(i >> 3)
{
std::cout << "i is less than 8" << std::endl
}
Which proves that, in C/C++, the if expression is not limited to == and =. Anything will do, as long as it can be evaluated as true or false (C++), or zero non-zero (C/C++).
About valid uses
Back to the non-quoted answer.
The following notation:
if(MyObject * p = findMyObject())
{
// uses p
}
enables the user to declare and then use p inside the if. It is a syntactic sugar... But an interesting one. For example, imagine the case of an XML DOM-like object whose type is unknown well until runtime, and you need to use RTTI:
void foo(Node * p_p)
{
if(BodyNode * p = dynamic_cast<BodyNode *>(p_p))
{
// this is a <body> node
}
else if(SpanNode * p = dynamic_cast<SpanNode *>(p_p))
{
// this is a <span> node
}
else if(DivNode * p = dynamic_cast<DivNode *>(p_p))
{
// this is a <div> node
}
// etc.
}
RTTI should not be abused, of course, but this is but one example of this syntactic sugar.
Another use would be to use what is called C++ variable injection. In Java, there is this cool keyword:
synchronized(p)
{
// Now, the Java code is synchronized using p as a mutex
}
In C++, you can do it, too. I don't have the exact code in mind (nor the exact Dr. Dobb's Journal's article where I discovered it), but this simple define should be enough for demonstration purposes:
#define synchronized(lock) \
if (auto_lock lock_##__LINE__(lock))
synchronized(p)
{
// Now, the C++ code is synchronized using p as a mutex
}
(Note that this macro is quite primitive, and should not be used as is in production code. The real macro uses a if and a for. See sources below for a more correct implementation).
This is the same way, mixing injection with if and for declaration, you can declare a primitive foreach macro (if you want an industrial-strength foreach, use Boost's).
About your typo problem
Your problem is a typo, and there are multiple ways to limit its frequency in your code. The most important one is to make sure the left-hand-side operand is constant.
For example, this code won't compile for multiple reasons:
if( NULL = b ) // won't compile because it is illegal
// to assign a value to r-values.
Or even better:
const T a ;
// etc.
if( a = b ) // Won't compile because it is illegal
// to modify a constant object
This is why in my code, const is one of the most used keyword you'll find. Unless I really want to modify a variable, it is declared const and thus, the compiler protects me from most errors, including the typo error that motivated you to write this question.
But is there a case where the warning should be ignored because it's a good way to use this "feature"? I don't see any code clarity reason possible so is there a case where its useful?
Conclusion
As shown in the examples above, there are multiple valid uses for the feature you used in your question.
My own code is a magnitude cleaner and clearer since I use the code injection enabled by this feature:
void foo()
{
// some code
LOCK(mutex)
{
// some code protected by a mutex
}
FOREACH(char c, MyVectorOfChar)
{
// using 'c'
}
}
... which makes the rare times I was confronted to this typo a negligible price to pay (and I can't remember the last time I wrote this type without being caught by the compiler).
Interesting sources
I finally found the articles I've had read on variable injection. Here we go!!!
FOR_EACH and LOCK (2003-11-01)
Exception Safety Analysis (2003-12-01)
Concurrent Access Control & C++ (2004-01-01)
Alternatives
If one fears being victim of the =/== typo, then perhaps using a macro could help:
#define EQUALS ==
#define ARE_EQUALS(lhs,rhs) (lhs == rhs)
int main(int argc, char* argv[])
{
int a = 25 ;
double b = 25 ;
if(a EQUALS b)
std::cout << "equals" << std::endl ;
else
std::cout << "NOT equals" << std::endl ;
if(ARE_EQUALS(a, b))
std::cout << "equals" << std::endl ;
else
std::cout << "NOT equals" << std::endl ;
return 0 ;
}
This way, one can protect oneself from the typo error, without needing a language limitation (that would cripple language), for a bug that happens rarely (i.e., almost never, as far as I remember it in my code).
There's an aspect of this that hasn't been mentioned: C doesn't prevent you from doing anything it doesn't have to. It doesn't prevent you from doing it because C's job is to give you enough rope to hang yourself by. To not think that it's smarter than you. And it's good at it.
Never!
The exceptions cited don't generate the compiler warning. In cases where the compiler generates the warning, it is never a good idea.
RegEx sample
RegEx r;
if(((r = new RegEx("\w*)).IsMatch()) {
// ... do something here
}
else if((r = new RegEx("\d*")).IsMatch()) {
// ... do something here
}
Assign a value test
int i = 0;
if((i = 1) == 1) {
// 1 is equal to i that was assigned to a int value 1
}
else {
// ?
}
My favourite is:
if (CComQIPtr<DerivedClassA> a = BaseClassPtr)
{
...
}
else if (CComQIPtr<DerivedClassB> b = BaseClassPtr)
{
...
}