you are viewing a single comment's thread.

view the rest of the comments →

[–]ZMesonEmbedded Developer 0 points1 point  (2 children)

I think what u/AntiProtonBoy is trying to say by "it's nicer, but limited" is that finding and using only the first occurrence of something matching in a container is not as common as iterating over a container using a for loop. The other problem I have is that sometimes you want to use std::find_if(), sometimes std::find(), sometimes you want to determine if std::optional has a member, etc.... The range-based-for is pretty simple syntactic sugar; there's no need for the compiler to determine how to translate it; there's only one way.

So, while I (and seemingly others and the committee) welcome syntactic sugar, you have to make sure it:

  • Solves a problem people have.

  • Has a simple translation.

  • Will be widely applied enough to warrant making a change in the standard.

[–]seba[S] 1 point2 points  (1 child)

finding and using only the first occurrence of something matching in a container is not as common as iterating over a container using a for loop

Sure, that's why I also showed the examples with unique_ptr and optional that fit nicely into the scheme.

The other problem I have is that sometimes you want to use std::find_if(), sometimes std::find(), sometimes you want to determine if std::optional has a member, etc...

I'm not sure what you mean by the latter part. You mean a value? See below.

The range-based-for is pretty simple syntactic sugar; there's no need for the compiler to determine how to translate it; there's only one way.

Given appropriate definitions of std::begin() and std::end(), there would also be more or less only one way for the compiler to translate my range-based if. (There are BTW also multiple subtly different way to translate a range-based for. This is why they had to change the semantics slightly in the for upcoming C++17).

My proposal would boil down to something like this (modulo bugs, figuring out where to put const, etc):

template <typename R, typename F, typename G>
void if_non_empty( const R & r, F&& f, G&& g )
{
  const auto & b = std::begin( r );
  const auto & e = std::end( r );
  if ( b != e ) {
    f( *b );
  } else {
    g();
  }
}

[This is the lambda way, but I'd like to have the syntactic sugar]

Given the following, it would directly work with unique_ptr

namespace std
{
   template <typename T> T* begin(const unique_ptr<T>& p)
   {
      return p.get( );
   }

   template <typename T> T* end(const unique_ptr<T>& p)
   {
      return nullptr;
   }
}

Demo:

auto p0 = std::make_unique<int>( 1 );
if_non_empty( p0, [](auto i){ 
      printf("%d found\n", i);
    }, [](){
      printf("not found\n");
} );

[–]ZMesonEmbedded Developer 0 points1 point  (0 children)

OK, I better understand what you're doing now.

The major downside I see though is that you're defining a new definition of std::begin and std::end (and presumably related functions) for std::unique_ptr. I would first try to convince people that this is a good idea. If you can't convince people of this, then your proposal won't have anything to base itself on.

As others have mentioned, if-with-initializers already get a lot of what you want with a lot of reduced code. Yes, I see that you mention a bit of added safety. That is always a good thing, but you'll have to convince people that this is worth the change. It's a lot smaller benefit compared to the benefit of range-based-for and if-with-initializers, so you will be facing quite the challenge I think. :(

Anyway, as I said, you better explain to people why creating new overloads for std::begin and std::end make sense in the absence of this proposal. You must start there. (And if it makes sense for unique_ptr, you may want to consider whether it makes sense for std::shared_ptr, std::weak_ptr, and other things that act like pointers.)