/////////////////////////
typedef int T;
T a() {
return 0;
}
int main()
{
if(T p = a()) {
p; // in scope, not null - compiles OK
}
else {
p; // in scope - compiles OK
}
p; // out of scope - error: undeclared identifier
// Note that an inverse condition, where p would
// have an actually useful value inside
// the else block won't compile:
if(!(T p = a())) { // syntax error
p;
}
return 0;
}
//////////////////////////
Now, what kind of sense does it make that p is still in scope in the
else block? It's always false/0/NULL there anyway! *shakeshead*
cheers,
Martin
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Huh? That's just not even remotely true (pun intended). You get into the
'else' branch when the result of 'T -> bool' conversion evaluates to
'false'. Just because the result of that conversion evaluates to 'false'
does not mean that 'T' object itself is "false/0/NULL" or in any way
useless.
For example, 'T' could be a complex object with lots of data in it and
the fact that 'T -> bool' evaluates to 'false' might simply mean that
the data stored in 'T' should be processed differently.
--
Best regards,
Andrey Tarasevich
The object can be useful in the else - consider a class that works a bit
like std streams do when it comes to error deteection:
#include <iostream>
using namespace std;
struct T {
T( const char * s ) {
if ( s == 0 ) {
err = 1;
}
else if ( * s == 0 ) {
err = 2;
}
else {
err = 0;
}
}
operator bool() const {
return err == 0;
}
int errcode() const {
return err;
}
int err;
};
T f() {
return T("");
}
int main() {
if ( T t = f() ) {
cout << "ok\n";
}
else {
cerr << "error " << t.errcode() << "\n";
}
}
Neil Butterworth
I don't see the convincing advantage over
if (T p = !a()) {
!p;
}
> return 0;}
>
> //////////////////////////
>
> Now, what kind of sense does it make that p is still in scope in the
> else block? It's always false/0/NULL there anyway! *shakeshead*
There are several reasons for this:
1) It ensures a consistency between if and else, because both
have the same scoping level and are based one the same "root"
element - the /condition/ of the selection-statement.
2) Your argument that inside the else clause the value of p is
always false/0/NULL is misleading for two reasons:
a) With the same argument one could point out that inside
the if-statement p is always true/!0/!NULL, so this is no
convincing argument at all.
b) If p is not a scalar, but a user-defined class-type, it may
have completely different state values within the if- and
the else-statement, e.g.:
class State {
std::vector<WhatEver> data;
Something other;
public:
...
operator bool() const {
return /.../; // Compute result from internal state
}
};
State a();
void test() {
if (const State& s = a()) {
// use s here
} else {
// use s here
}
}
HTH & Greetings from Bremen,
Daniel Kr�gler
>
> Sometimes the C++ syntax really leaves me baffled:
>
> /////////////////////////
> typedef int T;
>
> T a() {
> return 0;
> }
>
> int main()
> {
> if(T p = a()) {
> p; // in scope, not null - compiles OK
> }
> else {
> p; // in scope - compiles OK
> }
> p; // out of scope - error: undeclared identifier
>
> // Note that an inverse condition, where p would
> // have an actually useful value inside
> // the else block won't compile:
> if(!(T p = a())) { // syntax error
> p;
> }
>
> return 0;
> }
> //////////////////////////
>
> Now, what kind of sense does it make that p is still in scope in the
> else block? It's always false/0/NULL there anyway! *shakeshead*
>
You can have user defined types that have more state than just a single
"false".
Take for example
if(Connection c = connect("addy.com")) {
print(c.getResponse());
} else {
print("Error happened: " + c.getError());
> Now, what kind of sense does it make that p is still in scope in the
> else block? It's always false/0/NULL there anyway! *shakeshead*
The if condition evaluates the p object as bool. The object might have
other state that is useful in the else block.
null?? p is int, not a pointer. I don't even understand what is compared
there.
> }
> else {
> p; // in scope - compiles OK
> }
> p; // out of scope - error: undeclared identifier
>
> // Note that an inverse condition, where p would
> // have an actually useful value inside
> // the else block won't compile:
> if(!(T p = a())) { // syntax error
> p;
> }
>
> return 0;
> }
> //////////////////////////
>
> Now, what kind of sense does it make that p is still in scope in the
> else block? It's always false/0/NULL there anyway! *shakeshead*
What kind of sense does it make to write such code? Unless you are
trying to compete here:
http://www.ioccc.org/main.html
I find this much clearer:
const T p = a();
if( 0 == a) {
p; // in scope, not 0 - compiles OK
}
else {
p; // in scope - compiles OK
}
--
Bolje je ziveti sto godina kao bogatun, nego jedan dan kao siromah!
> if(T p = a()) {
> p; // in scope, not null - compiles OK
> }
In your example you made T an int. p cannot be null in the usual
sense of the word null because p is not a pointer at all, so calling
it null or not null doesn't make sense. Now as it turns out in your
particular example, p will always be 0. This is probably where some
confusion can come from, because C++ can treat 0 as an int or it can
interpret it as a null ptr (commonly called NULL) or as false.
> Now, what kind of sense does it make that p is still in scope in the
> else block?
The rules that decide whether a variable is in scope are not related
to the value of the variable. The concept of "scope" means what areas
of the code can access a name. It might also help to think of the
scope of a variable as its lifetime. In your example, you create a
variable that has a scope of the if/else block. A similar example
more common might be a for loop:
for(int x=0; x<10; x++)
{
x *= 2; // ok because x is still in scope
}
x = 0; // not ok because the scope of x ended with the for loop
Your example could be modified in a way that makes it useful to have p
available in the if and the else block by a slightly different a():
#include <cstdlib>
T a() {
return rand(); // now it could be 0 or it could be something else
}
> // Note that an inverse condition, where p would
> // have an actually useful value inside
> // the else block won't compile:
> if(!(T p = a())) { // syntax error
> p;
> }
Yes here the (T p = a()) is what is getting you. You might try:
{
T p = a();
if(!p) {
p;
Try:
if ( T p = a() == 0) { // OK
p;
}
Greg
After this, p has been assigned !a() which is either true or false,
except if the T had a user defined !operator that returns something else.
>> return 0;}
>>
>> //////////////////////////
>>
>> Now, what kind of sense does it make that p is still in scope in the
>> else block? It's always false/0/NULL there anyway! *shakeshead*
>
> There are several reasons for this:
>
> 1) It ensures a consistency between if and else, because both
> have the same scoping level and are based one the same "root"
> element - the /condition/ of the selection-statement.
>
OK. That's at least some argument. I don't think it's convincing, but
hey :-)
> 2) Your argument that inside the else clause the value of p is
> always false/0/NULL is misleading for two reasons:
>
> a) With the same argument one could point out that inside
> the if-statement p is always true/!0/!NULL, so this is no
> convincing argument at all.
>
For a typical pointer "!NULL" can be like 2^32-2 values? So I guess it's
rather useful to have the actual value, wouldn't you agree?
> b) If p is not a scalar, but a user-defined class-type, it may
> have completely different state values within the if- and
> the else-statement, e.g.:
>
> class State {
> (...)
I do not agree. Or rather, I'd say that most of the time when a Type T
is able to be evaluated in boolean context then it is either a numeric
type or a handle-like type and and you only need the !"null" values of
such a type.
cheers,
Martin
Wrong. What this actually does is:
if ( T p = (a() == 0) ) { // Not what you want
cheers,
Martin
if ( T t = something )
very useful. You can't compare "t" to anything,
so it's only usable if the condition you want to
check is whether t evaluates to true vs. false.
For example, something like:
if ( ( T::iterator iter = t.position() ) != t.end() )
{
// use iter
}
would be very useful, but is not well-formed.
The version that _is_ legal comes up so rarely
in my code that it has not been worth
adding this idiom to my coding style.
--
> Daniel Kr�gler wrote:
>> b) If p is not a scalar, but a user-defined class-type, it may
>> have completely different state values within the if- and
>> the else-statement, e.g.:
>>
>> class State {
>> (...)
>
> I do not agree. Or rather, I'd say that most of the time when a Type T
> is able to be evaluated in boolean context then it is either a numeric
> type or a handle-like type and and you only need the !"null" values of
> such a type.
But "most of the time" is still not "always". Why do you want to forbid
those rare cases that you get for free by giving a variable that was
declared in the condition of an if-statement the natural scope of the
entire if-statement (which includes the else clause).
>
> cheers,
> Martin
>
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://c-faq.com/
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
If you consider the above to be a valid argument in the context of this
discussion, then the question you should be asking is why C++ even
allows non-scalar objects as logical expressions in 'if', not why the
scope of the declared object extends into the 'else' branch.
If you think that the ability to write 'if (a)' where 'a' is not a
scalar object is completely useless - just say so. (I will disagree though).
If you think that 'if (a)' with non-scalar 'a' has its uses, then you
have to agree that the ability to access the declared object in the
'else' branch of 'if (T t = ...)' is a perfectly logical and useful
feature.
It is either this or that. There's no way around it.
--
Best regards,
Andrey Tarasevich
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
That it doesn't work nicely with iterators is, in my opinion, a sign that
iterators are badly designed. It's a consequence of what ought to be a
single thing being split over two objects. If instead you use a range
object to encapsulate a pair of iterators, you can arrange for boolean
evaluation to return false when the range is empty. That would make the
"if" idiom quite useful. Eg:
if (auto r = find_range( v.all(), item )) {
// We found item at r.
}
> The version that _is_ legal comes up so rarely
> in my code that it has not been worth
> adding this idiom to my coding style.
I have found the opposite. I found the idiom so convenient for pointers,
I've added a safe-bool member to several of my own classes so I can use
it with them, too. Anything which has an error or empty state can
benefit.
-- Dave Harris, Nottingham, UK.
I agree that it's frustrating, but I don't think there's a perfect
solution. As others have said, it's not too difficult to accept p
being in scope in the else block, even though it requires a more
abstract perspective. p being in scope after the if would sometimes
be useful, but it's often good that it's not - you can test something,
deal with it and not mess up the enclosing scope, much as per "for
(int i =...", which went through the same tuning process, but which is
sometimes not what you want either. The language could have some
keyword to indicate the scope you'd like the variable in, and it could
allow variables to be declared at any point in the conditional
expression, but both approaches suffer from inviting the kind of
complexity of code that leads to more cons re maintenance problems
than pros for concision and localisation. If you're scanning back
through a function for the place where a variable is introduced, you
can currently at least rely on it being close to the left (assuming
typical indentation habits)... that's actually worth quite a bit.
Cheers,
Tony
Vladimir Jovic schrieb:
> Martin B. wrote:
>> if(T p = a()) {
>> p; // in scope, not null - compiles OK
>
> null?? p is int, not a pointer. I don't even understand what is compared
> there.
The result of a() is assigned to a new variable p of type T. Then the
value of p is evaluated as a condition, which converts p to bool. For
builtin type such as numbers and pointers it will compare p to 0.
Otherwise some custom conversion operator is called.
> What kind of sense does it make to write such code? Unless you are
> trying to compete here:
> http://www.ioccc.org/main.html
I find the syntax most useful in cases where I have to use dynamic_cast
(instead of the visitor pattern):
void foo(Base *const param)
{
if(T *const inst = dynamic_cast<Class1*>(param)) {
//...
} else if(T *const inst = dynamic_cast<Class2*>(param)) {
//...
}
...
> I find this much clearer:
> const T p = a();
> if( 0 == a) {
> p; // in scope, not 0 - compiles OK
> }
> else {
> p; // in scope - compiles OK
> }
With this notation the dynamic cast use case will become much more verbose.
Frank
--