17

Consider the following code:

template <typename... Types>
struct list
{
    template <typename... Args>
    list(Args...) 
    {
        static_assert(sizeof...(Types) > 0);
    }
};

template <typename... Args>
list(Args...) -> list<Args...>;

int main()
{
    list l{0, 0.1, 'a'};
}

I would expect decltype(l) to be list<int, double, char>. Unfortunately, g++ 7.2 and g++ trunk fail the static assertion. clang++ 5.0.0 and clang++ trunk compile and work as expected.

godbolt.org conformance view

Is this a g++ bug? Or Is there a reason why the deduction guide should not be followed here?


Adding a SFINAE constraint on the constructor seems to provide the desired behavior:

template <typename... Args, 
          typename = std::enable_if_t<sizeof...(Args) == sizeof...(Types)>>
list(Args...) 
{
    static_assert(sizeof...(Types) > 0);
}

godbolt.org conformance view

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • I think your code might be ill-formed... for `sizeof...(Types) == 0` there is no viable instantiation of your templated constructor... – W.F. Oct 20 '17 at 11:36
  • I'm struggling to see this as anything other than a gcc bug. using the previous method of a make_list() function works fine. https://godbolt.org/g/vag3rD – Richard Hodges Oct 20 '17 at 11:49
  • @RichardHodges naturally with your method g++ doesn't try to instantiate `list<>`. – n. m. could be an AI Oct 20 '17 at 12:16
  • Hmm. When doing the overload resolution, there's also a hypothetical function `template list deducer(Args...);` - which is more specialized than the deduction guide's `template list deducer(Args...);` isn't it? – aschepler Oct 20 '17 at 12:22
  • No wait, I guess neither is more specialized... – aschepler Oct 20 '17 at 12:26
  • @n.m. Why would the compiler try to instanciate list<> at all? it's not indicated. – Richard Hodges Oct 20 '17 at 12:28
  • @RichardHodges Long shot but doesn't this: [\[temp.inst\]/7](http://eel.is/c++draft/temp.inst#7) suggest that it can instantiate anything? – W.F. Oct 20 '17 at 12:55
  • also replacing the guide with `template list(T,Args...) -> list;` seems providing the desired behavior, giving a more specialized version of the implicit function mentioned by @aschepler – Massimiliano Janes Oct 20 '17 at 13:06

1 Answers1

13

This is gcc bug 80871. The issue is, we end up with this set of candidates for deduction:

template <class... Types, class... Args>
list<Types...> __f(Args... ); // constructor

template <class... Args>
list<Args...>  __f(Args... ); // deduction-guide

Both are valid (Types... can deduce as empty in the first case), but the call here should be ambiguous - neither is more specialized than the other. Types... does not participate in ordering here (similar to the example in [temp.deduct.partial]/12). So the correct behavior is to proceed to the next tiebreaker, which favors deduction-guides. Hence, this should be a list<int, double, char>.

However, gcc's behavior is to favor the constructor, hence the static_assert triggers becuase Types... would indeed be empty in that situation.

Barry
  • 286,269
  • 29
  • 621
  • 977