Date: Thu, 10 Apr 2025 18:53:41 +0100
On Thu, 10 Apr 2025, 13:59 Frederick Virchanza Gotham via Std-Proposals, <
std-proposals_at_[hidden]> wrote:
> On Thu, Apr 10, 2025 at 12:20 PM Giuseppe D'Angelo wrote:
> >
> > template <std::range T>
> > void f(T &&& arg)
> > {
> > auto b = ranges::begin(arg);
> > auto e = ranges::end(arg);
> >
> > use(b, e);
> >
> > assert(check(arg));
> > }
>
>
> If NDEBUG is defined, the above code becomes:
>
> template <std::range T>
> void f(T &&arg)
> {
> auto b = ranges::begin(arg);
> auto e = ranges::end( forward<T>(arg) );
>
> use( b, e );
>
> (void)0;
> }
>
> Otherwise it becomes:
>
> template <std::range T>
> void f(T &&arg)
> {
> auto b = ranges::begin(arg);
> auto e = ranges::end(arg);
>
> use( b, e );
>
> check( forward<T>(arg) ) || __assert_failure( "check(arg)");
> }
>
> So when compiling in Debug mode, you'll occasionally get an
> L-value-accepting function being called instead of an
> R-value-excepting function. This isn't a big deal.
>
Clearly not a view that everybody shares.
> I think this is preferable to writing your function as follows:
>
> template <std::range T>
> void f(T &&arg)
> {
> auto b = ranges::begin(arg);
>
> #ifdef NDEBUG
> auto e = ranges::end( forward<T>(arg) );
> #else
> auto e = ranges::end(arg);
> #endif
>
> use(b, e);
>
> assert(check(arg));
> }
>
> Perhaps in this situation you would even write a macro:
>
> #ifdef NDEBUG
> # define FORWARD_IN_RELEASE_MODE(x) (std::forward<decltype(x)>(x))
> #else
> # define FORWARD_IN_RELEASE_MODE(x) (x)
> #endif
>
Or you could write a macro FORWARD_ON_LAST_USE which would expand to
std::forward<decltype(x)>(x) and you could use that in your functions on
the last use. If somebody adds another call later so you have:
foo(arg);
bar(FORWARD_ON_LAST_USE(arg));
baz(FORWARD_ON_LAST_USE(arg))
This is obviously wrong and easy to spot during review.
Problem solved, no need for a new kind of reference with magical properties
that are only useful in one specific context.
> And use it as follows:
>
> template <std::range T>
> void f(T &&arg)
> {
> auto b = ranges::begin(arg);
> auto e = ranges::end( FORWARD_IN_RELEASE_MODE(arg) );
>
> use(b, e);
>
> assert( check(arg) );
> }
>
> It would be handier and less prone-to-introducing-bugs to use an
> autoforward reference as follows:
>
> template <std::range T>
> void f(T &&&arg) // trigraph &&& for auto-forwarding
> {
> auto b = ranges::begin(arg);
> auto e = ranges::end(arg); // This will be forwarded if NDEBUG is
> defined
>
> use(b, e);
>
> assert(check(arg));
> }
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
std-proposals_at_[hidden]> wrote:
> On Thu, Apr 10, 2025 at 12:20 PM Giuseppe D'Angelo wrote:
> >
> > template <std::range T>
> > void f(T &&& arg)
> > {
> > auto b = ranges::begin(arg);
> > auto e = ranges::end(arg);
> >
> > use(b, e);
> >
> > assert(check(arg));
> > }
>
>
> If NDEBUG is defined, the above code becomes:
>
> template <std::range T>
> void f(T &&arg)
> {
> auto b = ranges::begin(arg);
> auto e = ranges::end( forward<T>(arg) );
>
> use( b, e );
>
> (void)0;
> }
>
> Otherwise it becomes:
>
> template <std::range T>
> void f(T &&arg)
> {
> auto b = ranges::begin(arg);
> auto e = ranges::end(arg);
>
> use( b, e );
>
> check( forward<T>(arg) ) || __assert_failure( "check(arg)");
> }
>
> So when compiling in Debug mode, you'll occasionally get an
> L-value-accepting function being called instead of an
> R-value-excepting function. This isn't a big deal.
>
Clearly not a view that everybody shares.
> I think this is preferable to writing your function as follows:
>
> template <std::range T>
> void f(T &&arg)
> {
> auto b = ranges::begin(arg);
>
> #ifdef NDEBUG
> auto e = ranges::end( forward<T>(arg) );
> #else
> auto e = ranges::end(arg);
> #endif
>
> use(b, e);
>
> assert(check(arg));
> }
>
> Perhaps in this situation you would even write a macro:
>
> #ifdef NDEBUG
> # define FORWARD_IN_RELEASE_MODE(x) (std::forward<decltype(x)>(x))
> #else
> # define FORWARD_IN_RELEASE_MODE(x) (x)
> #endif
>
Or you could write a macro FORWARD_ON_LAST_USE which would expand to
std::forward<decltype(x)>(x) and you could use that in your functions on
the last use. If somebody adds another call later so you have:
foo(arg);
bar(FORWARD_ON_LAST_USE(arg));
baz(FORWARD_ON_LAST_USE(arg))
This is obviously wrong and easy to spot during review.
Problem solved, no need for a new kind of reference with magical properties
that are only useful in one specific context.
> And use it as follows:
>
> template <std::range T>
> void f(T &&arg)
> {
> auto b = ranges::begin(arg);
> auto e = ranges::end( FORWARD_IN_RELEASE_MODE(arg) );
>
> use(b, e);
>
> assert( check(arg) );
> }
>
> It would be handier and less prone-to-introducing-bugs to use an
> autoforward reference as follows:
>
> template <std::range T>
> void f(T &&&arg) // trigraph &&& for auto-forwarding
> {
> auto b = ranges::begin(arg);
> auto e = ranges::end(arg); // This will be forwarded if NDEBUG is
> defined
>
> use(b, e);
>
> assert(check(arg));
> }
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
Received on 2025-04-10 17:54:02