Недопустимый вывод аргумента шаблона класса с производным классом
#include <utility>
template<class T1, class T2>
struct mypair : std::pair<T1, T2>
{ using std::pair<T1, T2>::pair; };
int main()
{
(void)std::pair(2, 3); // It works
(void)mypair(2, 3); // It doesn't work
}
Является ли это хорошо сформированным?
Можно ли выводить аргументы шаблона класса во втором случае, если конструкторы наследуются? Являются ли конструкторы std::pair
участвующими в создании неявных указателей вычитания для mypair
?
Мой компилятор - g++ 7.2.0.
Ответы
Ответ 1
Я думаю, что это ошибка gcc (или, по крайней мере, незначительный дефект формулировки основного языка).
Унаследованные конструкторы считаются конструкторами в соответствии с [namespace.udecl]/16:
Аналогично, конструкторы, которые вводятся с помощью объявления-объявления, рассматриваются как конструкторы производного класса при поиске конструкторов производного класса ([class.qual]) или формирования набора кандидатов перегрузки ([ over.match.ctor], [over.match.copy], [over.match.list])
Этот список условий технически не включает [over.match.class.deduct], но импликация заключается в том, что конструкторы базового класса являются конструкторами производного класса. И правило в [over.match.class.deduct] должно учитывать:
Если C
определен, для каждого конструктора C
, шаблон функции со следующими свойствами [...]
Мы ищем конструкторы производного класса, только не в любом из перечисленных случаев. Но этот пример должен работать концептуально:
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
Ответ 2
Краткая история: в стандарте нет правила, в котором говорится, как это будет работать, ни правила, в котором говорится, что он не работает. Таким образом, GCC и Clang консервативно отклоняют, а не изобретают (нестандартное) правило.
Длинная история: mypair
pair
базовый класс является зависимым типом, поэтому поиск его конструкторов не может быть успешным. Для каждой специализации mytype<T1, T2>
соответствующие конструкторы pair<T1, T2>
являются конструкторами mytype
, но это не правило, которое может быть осмысленно применено к шаблону до создания экземпляра вообще.
В принципе, может существовать правило, в котором говорится, что вы смотрите на конструкторы основного шаблона pair
в этой ситуации (так же, как мы это делаем при поиске конструкторов самого mypair
для вывода аргумента шаблона шаблона) но в настоящее время такого стандарта не существует в стандарте. Такое правило быстро падает, однако, когда базовый класс становится более сложным:
template<typename T> struct my_pair2 : std::pair<T, T> {
using pair::pair;
};
Какие конструкторы должны быть введены здесь условно? И в таких случаях я думаю, что разумно ясно, что этот поиск не может работать:
template<typename T> struct my_pair3 : arbitrary_metafunction<T>::type {
using arbitrary_metafunction<T>::type::type;
};
Возможно, мы получим изменение правила, чтобы позволить вычет через ваши my_pair
и my_pair2
выше, если/когда мы получаем правила вывода аргументов шаблона шаблона шаблона для шаблонов псевдонимов:
template<typename T> using my_pair3 = std::pair<T, T>;
my_pair3 mp3 = {1, 2};
Сложности, связанные с этим, во многом такие же, как и в случае с наследованным конструктором. Фейсал Вали (один из других разработчиков аргумента шаблона аргументов класса) имеет конкретный план, как заставить такие случаи работать, но комитет С++ еще не обсуждал это расширение.