0

Let's say I have a function that wants to deduce template template parameters to handle types that encapsulate other types, like std::optional:

template <template <typename> typename M, typename T>
void Foo(const M<T>& input);

// Deduces M = std::optional, T = int
Foo(std::make_optional(0));

This is great, but it accepts the input only as an lvalue reference. What if I want to accept any sort of reference, and pass it through to Bar with perfect forwarding? Without the deduction of M this is easy:

template <Input>
void Foo(Input&& input) {
  Bar(std::forward<Input>(input));
}

But now I've lost the deduction of M. Is there any way to deduce M and get something as convenient as a forwarding reference? I would like to avoid defining the function four to eight times if Bar needs to know the template M:

template <template <typename> typename M, typename T>
void Foo(M<T>& input);
  Bar<M>(input);
}

template <template <typename> typename M, typename T>
void Foo(const M<T>& input);
  Bar<M>(input);
}

template <template <typename> typename M, typename T>
void Foo(M<T>&& input);
  Bar<M>(std::move(input));
}

template <template <typename> typename M, typename T>
void Foo(const M<T>&& input);
  Bar<M>(std::move(input));
}
jacobsa
  • 5,719
  • 1
  • 28
  • 60
  • That's definitely the same subject, thanks. It doesn't exactly answer the question of "is there a more elegant way to do this than overloading the function". I guess the answer is no? – jacobsa Nov 07 '22 at 03:59
  • I'm curious as to why you would need to do this. Generally, when you are forwarding a parameter to someone else, you don't need to interact with it in any meaningful way. That's kind of the point: you *don't care* what type it is; you just need to hand it to someone else. Once you need to care, it stops being forwarding. – Nicol Bolas Nov 07 '22 at 04:05
  • This is for the sake of looking up a specialization of a struct that knows how to deal with the particular encapsulation. For example if I want to define an `Fmap` function that can map `std::optional` to `std::optional` but also `std::vector` to `std::vector`, and its implementation is essentially `return internal::FunctorOps::Fmap(std::forward??>(input))`, with the real intelligence in a hidden `FunctorOps` struct specialization. – jacobsa Nov 08 '22 at 07:16
  • "*but also `std::vector` to `std::vector`*" That's not possible; `vector` takes two template parameters, even if one of them can be defaulted. – Nicol Bolas Nov 08 '22 at 14:18
  • Yes, sure, bad example. The point still applies; it should work for 1-element templates for which the struct is specialized. – jacobsa Nov 09 '22 at 19:47
  • Why not make it work for *types* for which the struct is specialized? Why do you specifically need them to be "1-element templates"? – Nicol Bolas Nov 09 '22 at 20:13
  • The `FunctorOps` struct is about how to handle a specific type of functor, in Haskell's terminology (something like `std::optional`). For example how to implement `fmap` and `pure` for `std::optional`. Logically it's tied to `std::optional` in general, not `std::optional` for a specific `T`. But you're right, I think I see how I could instead define it for `std::optional` (at the cost of more template instantiation compiler CPU time) in particular and get what I want. Thanks! – jacobsa Nov 09 '22 at 23:52

0 Answers0