2

We have two templated classes, Class1 has a nested class. Class2 needs to be constructed / converted from that nested class object.

template<typename T> struct Class1{    
    Class1() = default;
    class Inner{};
};

template<typename T> struct Class2{
    Class2() = default;
    template<typename T2> Class2(const Class1<T2>&) {}
    template<typename T2> Class2(const typename Class1<T2>::Inner&) {}
};

void foo(const Class2<int>&){}

...

Class1<int> c1;
Class1<int>::Inner i1;

foo( c1);
foo( i1); // <===================ERROR

Error text is:

error: invalid initialization of reference of type ???const Class2<int>&??? from expression of type ???Class1<int>::Inner???

Why do I get this error? Constructing from Class1 works. Constructing from Inner if the classes are not templates also works.

Christophe
  • 68,716
  • 7
  • 72
  • 138
AdyAdy
  • 988
  • 6
  • 19

1 Answers1

1

There is no way for the second constructor (the one taking an Inner) to ever be called. Since the template parameter T2 appears in a non-deduced context, to the left of a scope resolution operator that names a dependant type, it must be specified explicitly.

But template arguments of a templated constructor cannot be provided explicitly! They must be deduced.

As such, substitution always fails for the second c'tor. Only the first c'tor ever makes it to overload resolution. And that overload resolution sees that you attempt to bind a Class2<int>::Inner object to a const Class2<int>&. That reference simply cannot bind.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Thank you! I'm trying to understand this concept of "non-deduced context". In this case the compiler knows that `i1` is actually `Class1::Inner` so why can't it call the right constructor? In any case, is there no workaround to achieve what I want? – AdyAdy Oct 27 '18 at 15:54
  • @AdyAdy - A compiler can't rely on that information. You can [read more about it](https://stackoverflow.com/q/25245453/817643). – StoryTeller - Unslander Monica Oct 27 '18 at 17:27
  • In that example they call a function as `g(double{})`. I think that is different since I can't call `foo(Inner{})`. Inner always comes from `Class1`. I'm not trying to argue just want to understand how this works. – AdyAdy Oct 27 '18 at 17:42
  • @AdyAdy - That is superficial. What's important to note is that the possibilty of specialization means that `::Inner` may not be what one expects. So the compiler may not deduce `T2`. I know it's counter intuitive. But forget for a sec your primary template. Imagine a malicious user that writes `template<> struct Class1 { typedef void Inner; };`. The language allows a user to do that. So the compiler can't deduce anything from `Inner`. – StoryTeller - Unslander Monica Oct 27 '18 at 18:01