I have measured a significant speedup in my application if my function foo
accepts primitive types by value instead of by universal (forwarding) reference. The speedup is lost however, if non-primitive types are not passed by universal reference. If foo
was accepting a fixed number of arguments, this would do the trick:
#include <cstdio>
#include <string>
#include <type_traits>
template <typename T>
requires (!std::is_scalar_v<std::decay_t<T>>)
auto foo(T&&){
std::puts("pass by universal reference");
}
template <typename T>
requires std::is_scalar_v<std::decay_t<T>>
auto foo(T){
std::puts("pass by value");
}
int main(){
foo(42); // pass by value
foo(std::string{"hello"}); // pass by universal reference
return 0;
}
However, the problem is that foo
accepts a template parameter pack, and some parameters might be primitives while others might not. Thus, foo
would have to deduce parameter types first and then, if they are not primitives, turn them into universal references. If foo
was a class template, this could be achieved using deduction guides:
#include <cstdio>
#include <string>
#include <type_traits>
template <typename T>
using MaybeRef =
std::conditional_t<std::is_scalar_v<std::decay_t<T>>, std::decay_t<T>, T&&>;
#include <tuple>
template <typename... Ts>
struct Foo {
Foo(Ts...) {
static_assert(std::is_same_v<std::tuple_element_t<0, std::tuple<MaybeRef<Ts>...>>, int>);
static_assert(std::is_same_v<std::tuple_element_t<1, std::tuple<MaybeRef<Ts>...>>, std::string&>);
std::puts("passed");
}
};
template <typename... Ts>
Foo(Ts&&...) -> Foo<MaybeRef<Ts>...>;
int main(){
std::string str = "hello";
Foo{42, str};
return 0;
}
However, I do not know how to achieve the same for my function foo
.
Is this even possible and how?
It's not possible to do this in current C++, but there are a number of relevant proposals:
in
keyword, which would tell the compiler to declare the parameter as either by-value or by-reference depending on which would be more efficient.