0

I have written a templated function which is designed to accept a lambda and a pack of arguments. I have declared the return type of my function to be the return type of the lambda. Is there a way I can specialize my templated function for certain lambda parameter return types?

The working portion of my code is as follows:

template <typename F, typename ... T>
auto crudeProfile(F f, T ... args) -> decltype(f(args...)) {...}

This works as I would expect it to. But I’d like to specialize its behavior on lambdas that return void. I have only so far come up with the following code:

template <typename F, typename ... T>
void crudeProfile(F f, T ... args) {...}

But the compiler complains about this when I try to use it:

1>Source.cpp(684): error C2668: 'crudeProfile': ambiguous call to overloaded function
1>  Source.cpp(637): note: could be 'void crudeProfile<main::<lambda_a7118596c99e3162db30942634c4e81e>,>(F)'
1>          with
1>          [
1>              F=main::<lambda_a7118596c99e3162db30942634c4e81e>
1>          ]
1>  Source.cpp(624): note: or       'void crudeProfile<main::<lambda_a7118596c99e3162db30942634c4e81e>,>(F)'
1>          with
1>          [
1>              F=main::<lambda_a7118596c99e3162db30942634c4e81e>
1>          ]
1>  Source.cpp(684): note: while trying to match the argument list '(main::<lambda_a7118596c99e3162db30942634c4e81e>)'

Even lambdas that return non-void result in this error, though the error will slightly change, to read "could be 'type' or void" (where 'type' is whatever the lambda return type is).

DWR
  • 888
  • 1
  • 8
  • 15

2 Answers2

1

Is there a way to partially specialize my templated function?

No you can't, but there's a workaround.

Probably wouldn't hurt for me to upgrade (to C++17) ...

So in C++17, thanks to if constexpr, you can write a very easy workaround for that without hurting your code too much as follows:

template <typename F, typename ... Ts>
decltype(auto) crudeProfile(F f, Ts ... args)
{
    if constexpr (std::is_same_v<std::invoke_result_t<F, Ts...>, void>)
    {
        // void
    }
    else
    {
        // not void
    }
}
Dean Seo
  • 5,486
  • 3
  • 30
  • 49
  • Oddly enough on VC2015 this compiles _only_ if I omit the `constexpr` portion of the if statement, change `is_same_v` to just `is_same`, and `invoke_result_t` to `result_of::type`. But it works! – DWR Aug 10 '18 at 04:13
  • @DavidRogers It might work without `constexpr`, but in that case you have to make sure the two implementations splitted by the *raw* `if` must be **NOT** ill-formed because the compiler tries to evaluate both, even if the control flow never meets the other condition. (Which is the significant difference between `if` and `if constexpr`) – Dean Seo Aug 10 '18 at 04:39
0

You can't partially specialize function templates and overloading is also out of the question1, so you have to resort on classes. Note that you can use if constexpr to simplify this a lot if you have access to C++17.

template <typename F, typename = void, typename... Ts>
struct helper {
    auto operator()(F f, Ts... args) -> decltype(f(args...)) {
      // your code
    }
};

template <typename F, typename... Ts>
struct helper<F, typename std::result_of<F(Ts...)>::type, Ts...> {
    void operator()(F f, Ts... args) {
        // specialization for void
    }
};

template <typename F, typename... Ts>
auto crudeProfile(F f, Ts... args) -> decltype(f(args...)) {
    return helper<F, Ts...>(f, args...);
}

Note: std::result_of was replaced by std::invoke_result, so if you can use that instead (C++17), use that instead.


1 Note that you can still use SFINAE, but I don't like this option, as that would require you to duplicate one binary condition (namely that the result of invoking f(args...) is a void).

If you want to do so instead of going through a helper, it is simply a matter of adding an additional template parameter to both overloads: typename std::enable_if<!std::is_same<void, typename std::result_of<F(Ts...)>::type>::value>::type* = nullptr>. Don't forget to remove the negation for the second overload.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
  • @DeanSeo Oops, sorry about that. Forgot that there cannot be any template parameters after a variadic. Fixed – Rakete1111 Aug 10 '18 at 03:48