lobelk
lobelk

Reputation: 323

How to separate type deduction from function parameter declaration?

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?

Upvotes: 3

Views: 110

Answers (1)

Brian Bi
Brian Bi

Reputation: 119164

It's not possible to do this in current C++, but there are a number of relevant proposals:

  • Herb Sutter's unpublished D0708 proposes (among other things) to add the 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.
  • Gašper Ažman et al. propose a more general mechanism called computed deduction in an also unpublished paper, D1107, which basically does exactly what you ask: to "separate type deduction from function parameter declaration".
  • Most recently, there is P2998 by James Touton, which would allow implementing your desired feature in library, since it would bring deduction guides into play when calling a function template.

Upvotes: 3

Related Questions