6

I have two code snippets with the same functionality, however one compiles and the other does not. Why?

This one compiles.

vector<int>::iterator it;
if ((it=find(v.begin(),v.end(),2))!=v.end()){
}

This one does not.

if ((vector<int>::iterator it=find(v.begin(),v.end(),2))!=v.end()){
}

This is the error I get.

main.cpp: In function ‘int main()’:
main.cpp:32:28: error: expected primary-expression before ‘it’
main.cpp:32:28: error: expected ‘)’ before ‘it’
main.cpp:44:5: error: expected ‘)’ before ‘return’

P.S. Be free to edit the title - I could not find anything descriptive.

Slazer
  • 4,750
  • 7
  • 33
  • 60
  • 12
    It's possible to define a variable in an `if` statement, but the type must follow *immediately* after the first open paren (e.g., `if (int x = whatever)`). The second open paren in your code breaks it (which, yes, renders the feature useless all too often). – Jerry Coffin Jul 29 '13 at 19:45
  • I came across this exact issue a couple of years ago, and got a good answer here. http://stackoverflow.com/questions/7836867/c-variable-declaration-in-if-expression – Neutrino Jul 29 '13 at 20:10

1 Answers1

2

You can only define a variable inside an if's () if the entire contents of the () starts with the variable declaration (ie, if it is nothing but a variable declaration).

What you are trying to do is both declare a variable, and then do a test on it. That isn't allowed.

You can either fall back to the two line version, or you can write up a container-based find that uses boost::optional or std::tr2::optional as follows:

namespace aux {
  using std::begin; using std::end;
  template<typename C> auto adl_begin( C&& c )->decltype( begin(std::forward<C>(c)) )
  { return begin(std::forward<C>(c)); }
  template<typename C> auto adl_end( C&& c )->decltype( end(std::forward<C>(c)) )
  { return end(std::forward<C>(c)); }
}
using aux::adl_begin; using aux::adl_end;

template<typename C, typename U>
optional< decltype( typename std::decay<*adl_begin( std::declval<C&>() )>::type ) >
my_find( C&& c, U&& u ) {
  auto it = std::find( adl_begin(c), adl_end(c), std::forward<U>(u) );
  if (it == adl_end(c))
    return {none_t};
  else
    return {it};
}

Instead of returning an iterator, the above returns an optional iterator, which when evaluated in a boolean context is false if the item isn't found.

You can now type:

if( auto op_it = my_find( v, 2 ) ) {
  auto it = *op_it; // optional, but saves on `*` elsewhere
  // code
}

and get roughly what you want.

optional is available in boost, in maybe std::tr2, and probably in std:: in C++14. The boost and std ones are slightly different.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524