When writing overloads for r-value I often end up with the same code. So to avoid repetition I use this trick
void f(int&&) {
std::println("&& overload");
}
void f(int const&) {
std::println("const& overload");
}
template<typename T1, typename T2>
concept no_repetition = std::same_as<std::remove_cvref_t<T1>, T2>;
void test(no_repetition<int> auto&& val) {
f(std::forward<decltype(val)>(val));
}
int main( ) {
int lval = 0;
test(0); //prints && overload
test(lval); //prints const& overload
return 0;
}
I am curious whether this approach has caveats. And if it does how can work around them.
You may have oversimplified your question, but in your example the test()
function is more restrictive than the f(...)
functions. Specifically:
double penetration = 69.;
f(69.); // works
f(penetration); // works, but f(int&&) is invoked !!
test(69.); // fails
test(penetration); // fails
You can rewrite your function using universal references:
void test5(auto&& val)
{
f(std::forward<decltype(val)>(val));
}
which will behave the same way as the f(...)
functions:
test5(69.); // works
test5(penetration); // works, but invokes f(int&&) !!
If you do want your test()
function to have a hard constraint on int
, then you have already come up with the best solution. In your comments you've mentioned that you don't understand how it works, so here is a quick explanation. If you have a concept, say:
template<typename T, typename U>
concept Uref = std::same_as<std::remove_cvref_t<T>, U>;
you can write an abbreviated function-template, like you did:
void test1(Uref<int> auto&& val)
{
f(std::forward<decltype(val)>(val));
}
This is equivalent to:
template< Uref<int> Int >
void test2(Int&& val)
{
f(std::forward<Int>(val));
}
Notice that in the above example (as well as in the abbreviated function-template) you specify one less parameter in Uref<int>
because the deduced type (Int
) is implicitly substituted as the first parameter to Uref
.
You could also re-write the above in a more verbose form:
template<typename Int>
requires Uref<Int, int>
void test3(Int&& val)
{
f(std::forward<Int>(val));
}
Link to working example: https://godbolt.org/z/76hP3P5zz