Class template argument deduction failed with derived class

旧街凉风 提交于 2019-12-05 01:03:59

I think this is a gcc bug (or at least a minor core language wording defect).

Inherited constructors do count as constructors, according to [namespace.udecl]/16:

Likewise, constructors that are introduced by a using-declaration are treated as though they were constructors of the derived class when looking up the constructors of the derived class ([class.qual]) or forming a set of overload candidates ([over.match.ctor], [over.match.copy], [over.match.list])

That list of conditions technically doesn't include [over.match.class.deduct], but the implication is that the base class constructors are constructors of the derived class. And the rule in [over.match.class.deduct] is to consider:

If C is defined, for each constructor of C, a function template with the following properties [...]

We are looking up constructors of the derived class, just not in any of the listed cases. But this example should work conceptually:

template <class T> struct base { base(T ) { } };
template <class T> struct derived : base<T> { using base<T>::base; };

base b = 4;    // ok
derived d = 4; // error
Richard Smith

The short story: there is no rule in the standard that says how this would work, nor any rule that says that it doesn't work. So GCC and Clang conservatively reject rather than inventing a (non-standard) rule.

The long story: mypair's pair base class is a dependent type, so lookup of its constructors cannot succeed. For each specialization of mytype<T1, T2>, the corresponding constructors of pair<T1, T2> are constructors of mytype, but this is not a rule that can be meaningfully applied to a template prior to instantiation in general.

In principle, there could be a rule that says that you look at the constructors of the primary pair template in this situation (much as we do when looking up constructors of mypair itself for class template argument deduction), but no such rule actually exists in the standard currently. Such a rule quickly falls down, though, when the base class becomes more complex:

template<typename T> struct my_pair2 : std::pair<T, T> {
  using pair::pair;
};

What constructors should be notionally injected here? And in cases like this, I think it's reasonably clear that this lookup cannot possibly work:

template<typename T> struct my_pair3 : arbitrary_metafunction<T>::type {
  using arbitrary_metafunction<T>::type::type;
};

It's possible we'll get a rule change to allow deduction through your my_pair and the my_pair2 above if/when we get class template argument deduction rules for alias templates:

template<typename T> using my_pair3 = std::pair<T, T>;
my_pair3 mp3 = {1, 2};

The complexities involved here are largely the same as in the inherited constructor case. Faisal Vali (one of the other designers of class template argument deduction) has a concrete plan for how to make such cases work, but the C++ committee hasn't discussed this extension yet.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!