32

Let's say we have a class template like this:

template<typename F>
class A
{
public:
  template<typename... Args>
  A(F f, Args... args)
  { /* Do something... */ }
};

And now I want to use it in some way like this one:

A<int(int)> a(::close, 1);

Now the question: is there any way to omit the <int(int)> because a compiler can know this information for the ::close? There is no need to save the "design" of the template.

As for concrete task, I need to design a template of a class. Objects of this class could take a function and parameters for this function at construction time and call this function later.

Serge Roussak
  • 1,731
  • 1
  • 14
  • 28

3 Answers3

35

No, you (currently) cannot. The standard way of doing this is by creating "make_like" function (such as make_pair, make_optional ...):

template<typename F, typename... Args>
A<std::decay_t<F>> make_A (F &&f, Args&&... args) {
    return {std::forward<F>(f), std::forward<Args>(args)...};
}

C++17 will introduce template argument deduction for class which will allow you to do exactly what you want (see also Barry's answer below).

Holt
  • 36,600
  • 7
  • 92
  • 139
  • I believe the proper way to make those factory functions is to use `std::decay` or a similar trait on `A`. Which would make it `A>`. See http://en.cppreference.com/w/cpp/utility/optional/make_optional or other `make_*` reference – KABoissonneault Jul 06 '16 at 18:28
  • @KABoissonneault Yes you are right, if I use universal references I should be using `std::decay`, I have updated the answer. – Holt Jul 06 '16 at 18:40
  • I didn't quite understand the question, and therefore the answer. Is the whole goal to be able to say `auto a(::close, 1)`? – Assimilater Jul 06 '16 at 19:30
  • @Assimilater OP's ask a way to automatically deduced class template parameter when constructing an instance (e.g. `A(::close, 1)`) which is not possible in c++14. The above answer allows you to deduce the template parameter using the `make_A` function, e.g. `auto a = make_A(::close, 1)` or `f(make_A(::close, 1))`. – Holt Jul 06 '16 at 19:32
  • Yes, that is the wording of the OP....maybe the wording is just confusing to me because I haven't done much with variadic templates....is this a direct consequence of that only? It seemed like it could apply more generally... – Assimilater Jul 06 '16 at 19:38
  • @Assimilater No, this is not a consequence of variadic templates. See `std::pair`, you cannot do `std::pair a(1, 1)` or `std::pair(1, 1)` because class template argument cannot be deduced so you use `std::make_pair(1, 1)` to have `T1` and `T2` deduced. – Holt Jul 06 '16 at 19:40
  • @Holt Ah, sorry that took a bit to click, thanks for the exp :) – Assimilater Jul 06 '16 at 19:43
16

Thanks to the adoption of template parameter deduction for constructors, in C++17, you'll be able to just write:

A a(::close, 1);

Before that, you'll just need to write a factory to do the deduction for you:

template <class F, class... Args>
A<std::decay_t<F>> make_a(F&& f, Args&&... args) {
    return {std::forward<F>(f), std::forward<Args>(args)...};
}

auto a = make_a(::close, 1);

This is a little verbose, but at least you don't need to worry about efficiency - there will be no copies made here thanks to RVO.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    Re: C++17: That is so weird. It seems like you should at least have to write `A a(::close,1);`. Though I guess I'll get used to it . . . – ruakh Jul 08 '16 at 18:11
11

You cannot omit the arguments of a template class, unless they are defaulted. What you can do is have a maker function which deduces the argument and forwards this argument to the template class, returning an object of the appropriate instantiation.

template<typename F, typename... Args>
A<F> make_A(F f, Args&&... args) {
    return A<F>(f, std::forward<Args>(args)...);
}
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274