Bug 80438 - Variadic template class argument deduction failure from variadic constructor deduction guide
Summary: Variadic template class argument deduction failure from variadic constructor ...
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 7.0.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
Keywords: rejects-valid
Depends on:
Reported: 2017-04-15 20:46 UTC by Vittorio Romeo
Modified: 2019-09-02 17:18 UTC (History)
3 users (show)

See Also:
Known to work:
Known to fail:
Last reconfirmed: 2018-11-14 00:00:00


Note You need to log in before you can comment on or make changes to this bug.
Description Vittorio Romeo 2017-04-15 20:46:17 UTC
Consider the following code:

    template <typename... Ts>
    struct foo
        static_assert(sizeof...(Ts) > 0);

        template <typename... Us>
        foo(Us... us) { }

    template <typename... Us>
    foo(Us...) -> foo<Us...>;

    int main() 
        foo y{0};

It fails to compile with the following output:

    prog.cc: In instantiation of 'struct foo<>':
    prog.cc:15:12:   required from here
    prog.cc:4:5: error: static assertion failed
        static_assert(sizeof...(Ts) > 0);

    prog.cc: In instantiation of 'foo<Ts>::foo(Us ...) [with Us = {int}; Ts = {}]':
    prog.cc:15:12:   required from here
    prog.cc:7:15: warning: unused parameter 'us#0' [-Wunused-parameter]
        foo(Us... us) { }

From the warning, it seems that `Ts` is incorrectly deduced as empty pack. This is why the `static_assert` fails.
I would expect `Ts` to be deduced as `{int}` (same to `Us`).

On wandbox:
Comment 1 Tony E Lewis 2018-12-26 20:30:56 UTC
Here are the notes I wrote before I spotted I was about to file a duplicate of Vittorio's report...

[C++17] constructor unexpectedly preferred over deduction guide in CTAD

Compiling the following under `-std=c++17` :

#include <type_traits>

template <typename...>
struct x {
    template <typename... Ts> x(const Ts &...) {}

template <typename... Ts> x(const Ts &...) -> x<Ts...>;

static_assert(   std::is_same_v< decltype( x{ 0 } ), x<int> > );
static_assert( ! std::is_same_v< decltype( x{ 0 } ), x<   > > );

...I see the two static_assert statements failing under GCC, implying that `x{ 0 }` is of type `x<>`, rather than `x<int>` as I would expect. These two static_assert statements pass under Clang.

I would expect `x<int>` because I would expect the deduction guide to take precedence over the constructor in determining the type of the expression `x{ 0 }`. AFAIU, this is what's meant by the point in [over.match.best] in the standard that says F1 is "better" than F2 if: 

> [...] F1 is generated from a deduction-guide [...] and F2 is not [...]

(from C++17 draft n4659.pdf and from working draft http://eel.is/c++draft/over.match.best )

I'm seeing this on the current trunk build of GCC on Godbolt (g++ 9.0.0 20181225 (experimental)).
Comment 2 Ed Catmur 2019-02-18 10:18:29 UTC
Workaround: add an N=1+ deduction guide:

    // existing
    template <typename... Us>
    foo(Us...) -> foo<Us...>;
    // additional, for g++
    template <typename U, typename... Us>
    foo(U, Us...) -> foo<U, Us...>;