#include <utility>
template <class... Types>
class my_tuple {
public:
// constexpr my_tuple() {}
// explicit my_tuple(const Types&...){}
template <class... UTypes> explicit my_tuple(UTypes&&...u){}
template <class U1, class U2>
my_tuple(const std::pair<U1, U2>& u) {}
};
// User-Defined Deduction Guides
// template<class... UTypes>
// my_tuple(UTypes...) -> my_tuple<UTypes...>; // #1
// template<class T1, class T2>
// my_tuple(std::pair<T1, T2>) -> my_tuple<T1, T2>; // #2
int main(){
int i = 1;
double d = 4.5;
auto mtp1 = my_tuple(i, d); // CTAD #1
const auto p = std::pair(i, d);
auto mtp2 = my_tuple(p); // CTAD #2
}
Класс my_tuple пытается напоминать конструкторы std::tuple. «Включив» только два конструктора, как в приведенном выше коде, CTAD работает. Таким образом, я интерпретировал, что в этом случае используются неявно сгенерированные направляющие вывода. Если я раскомментирую (т. е. включу) один/любой из двух других конструкторов, он все равно будет работать. Однако если я включу оба конструктора (то есть всего четыре), компиляция завершится неудачей как с GCC, так и с CLANG (multiple overloads instantiate to the same signature). На этом этапе, если я создаю (раскомментирую) пользовательские направляющие вывода (похожие на те, которые определены в библиотеке для std::tuple), все снова работает. Я также попробовал реализовать все соответствующие конструкторы, как в std::tuple, и поведение аналогичное.
Мой вопрос заключается в том, почему неявно сгенерированные направляющие вывода, кажется, работают в начальном состоянии программы, но впоследствии (когда все включено) необходимы пользовательские направляющие вывода. Моя цель — понять, нужны ли конструкции объектов mtp1 и mtp2 (т. е. строки, отмеченные CTAD #1 и CTAD #2) в определяемых пользователем направляющих вывода или они действительно использовали неявно сгенерированные.
Согласно CPPreference, руководства по выводам из библиотеки (соответствующие моим пользовательским здесь) предоставляются всего лишь to account for the edge cases missed by the implicit deduction guides, in particular, non-copyable arguments and array to pointer conversion. И два примера из моего кода, как мне кажется, не подходят для этих крайних случаев.
Примечание. Если я не ошибаюсь, это неявно сгенерированные конструкторами направляющие вывода (плюс дополнительный кандидат на вычет копирования [over.match.class.deduct], параграф 1.3) в исходном состоянии моего примера кода:
template<class... UTypes>
my_tuple(UTypes&&...) -> my_tuple<UTypes&&...>; // #1
template<class U1, class U2>
my_tuple(const std::pair<U1, U2>&) -> my_tuple<const std::pair<U1, U2>&>; // #2
template <typename T>
my_tuple(my_tuple<T>) -> my_tuple<T> // #3
Интересные ссылки:





Вы можете проверить получившиеся типы, это вас удивит Демо
my_tuple(i, d) приводит к my_tuple<>.
И как только дополнительные конструкторы будут раскомментированы, у вас будет два конструктора по умолчанию для my_tuple<>
constexpr my_tuple() {}
explicit my_tuple(const Types&...){} /* with empty pack Types */
в результате возникает ошибка.
При активации CTAD вы больше не генерируете проблемные my_tuple<> (которые, кстати, все еще проблематичны).
Types не выводится из этих конструкторов, поэтому пусто. std::tuple может сделать что-то вроде tuple(std::pair<Ts...>) requires (sizeof...(Ts) == 2).
Я действительно удивлен :) Я знаю, что пакет параметров шаблона также может принимать нулевые аргументы, но я не ожидал, что при вызове обоих исходных конструкторов аргументы
intиdoubleбудут правильно приняты. Например, вызов конструктора парыauto mtp2 = my_tuple(p);правильно вызываетtemplate <class U1, class U2> my_tuple(const std::pair<U1, U2>& u) {}(я проверил, что могу напечатать оба значения внутри конструктора), но затем возвращается типmy_tuple<>без пары, без целого числа и без двойного значения. Как это возможно?