Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will template parameter deduction for constructors available since C++17 allow explicitly specify some of the class template arguments?

Apart from the most obvious usage of template parameter deduction for constructors, I can imagine some more complex use cases where we deduce only part of the parameters of the template class e.g.:

std::pair<int> p(1, 2); // std::pair<int, int>

Although this construct would be natural consequence of the deduction of template parameters in functions I couldn't find any example of this kind of usage. Maybe it's because of the ambiguity in case of classes with variadic template arguments?

std::tuple<int> t(1, 2, 3); // std::tuple<int, int, int>

However this way introduced syntax wouldn't substitute too good "make_*" wrappers (cf. N3602) where the functionality is available for us...

like image 838
W.F. Avatar asked Aug 21 '16 18:08

W.F.


2 Answers

Apparently not. P0091's introduction claims that it does, but the actual wording says quite differently:

A template-name corresponding to a class template followed by a parenthesized expression-list...

This rules out the use of a template-name + some arguments and a "parenthesized expression-list". It is also ruled out grammatically in other parts of the wording.

So no, it won't be possible to specify some arguments while deducing others.

like image 131
Nicol Bolas Avatar answered Sep 21 '22 23:09

Nicol Bolas


Yes, but indirectly.

For other people who have a similar problem but want a valid work around, not a language lawyer:

This might be an esoteric example, but it actually matches very closely a real world application of this technique.

You use a deduction guide, from C++17 and an additional constructor which accepts a helper type. This is often still easier when you have a class with many many template arguments that should mostly be deduced.

https://en.cppreference.com/w/cpp/language/class_template_argument_deduction

template <std::size_t v>
struct SizeT{};

template <std::size_t v, typename T>
struct PrintToBuffer
{
    T const Dunno;

    PrintToBuffer(T const & pInput) : 
        Dunno(pInput)
    {}
    //This works for any class, as it just forwards to another constructor
    template <typename ... Args>
    PrintToBuffer(SizeT<v>, Args && ... pInput) : 
        PrintToBuffer(std::forward<Args>(pInput)...) 
    {}

    void Print(std::array<char, v> & pOutput)
    {
        for (auto & c : pOutput)
            c = Dunno;
    }
};

This can be played around with here:

https://godbolt.org/z/E8d1Dt

EDIT, Adding an example for tuple

And for an example with tuple:

template <typename ... Args>
struct Types{};

template <typename ... T>
struct Tuple : std::tuple<T...>
{
    using base_t = std::tuple<T...>;
    using base_t::base_t;

    template <typename ... Ignore>
    Tuple(Types<Ignore...>, T && ... pInput) : base_t(std::forward<T>(pInput)...) {}
};

template <typename ... Defined, typename ... Args>
Tuple(Types<Defined...>, Defined && ..., Args&& ...) -> Tuple<Defined..., Args...>;

With the compiler explorer here, note that both the deduced and defined template arguments are correctly constructed:

https://godbolt.org/z/VrT9Lf

Usage:

auto my_tuple = Tuple(Types<int, short>{}, 1, 1, 'a');

like image 42
David Ledger Avatar answered Sep 19 '22 23:09

David Ledger