Date: Thu, 10 Apr 2025 13:58:36 +0100
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.
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
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));
}
>
> 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.
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
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));
}
Received on 2025-04-10 12:58:49