6

Let's say we have a class like this with a user-defined deduction guide:

template<typename T, typename... Args>
struct Foo
{
    Foo(Args&&...) { std::cout << "just Args: " << __PRETTY_FUNCTION__ << std::endl; }
    Foo(Args&&..., T&&) { std::cout << "Args and T: " << __PRETTY_FUNCTION__ << std::endl; }
};

template<typename... Args>
Foo(Args&&...) -> Foo<Void, Args...>;

Now let's try to create an instance of this class: Foo foo { 10 };. What would be the deduced template arguments and what constructor will be called?

After some experimentation turns out it depends on the compiler. Namely, gcc 7 and clang 6 (from trunk) seem to choose the automatic guide, instantiating T with int and Args with an empty pack, hence the output is

Args and T: Foo<T, Args>::Foo(Args&& ..., T&&) [with T = int; Args = {}]

clang 5, on the other hand, chooses the user-defined guide:

just Args: Foo<Void, int>::Foo(Args &&...) [T = Void, Args = <int>]

Which choice is the right one, and how one might use the user-defined deduction guide in this case?

Full example available on wandbox.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
0xd34df00d
  • 1,496
  • 1
  • 8
  • 17

1 Answers1

3

Let's go from first principles. Trying to deduce from Foo{10} involves doing overload resolution on this set:

template <typename T, typename... Args>
Foo<T, Args...> __f(Args&&... ); // ctor #1

template <typename T, typename... Args>
Foo<T, Args...> __f(Args&&..., T&&); // ctor #2

template <typename... Args>
Foo<Void, Args...> __f(Args&&... ); // deduction guide

In the function synthesized from the first constructor, T is a non-deduced context. In the function synthesized from the second constructor, Args is a non-deduced context. So neither are viable. The deduction guide is viable, so it's trivially the best viable candidate, so we end up with a Foo<Void, int>.

Once we're there, we perform overload resolution again to pick a constructor. This is more straightforward, the first one is viable and the second isn't, so it should be invoked.

Any other behavior is a compiler bug (filed 83447).

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    Why is `Args` a non-deduced context in the function synthesized from ctor #2? It seems like it's totally deducible: an empty pack in this particular case. – 0xd34df00d Dec 17 '17 at 05:35
  • Right, I've thrown up a simple example that's close to what you've shown in the bugreport, and it compiled fine with all gcc's I tried and both clang 5 and 6, so I assumed it's correct. The link you gave proves it's not! Interestingly, clang <4 rejects the code. As for now, to make the code work with the existing compilers, I've added a dummy tag parameter to the second ctor, since that ctor is an implementation detail anyway and shouldn't be called by class users. Thanks a lot! – 0xd34df00d Dec 17 '17 at 17:44