Руководства по выводу кортежей в C++17 (CTAD): неявно генерируемые и определяемые пользователем

#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 

Интересные ссылки:

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете проверить получившиеся типы, это вас удивит Демо

my_tuple(i, d) приводит к my_tuple<>.

И как только дополнительные конструкторы будут раскомментированы, у вас будет два конструктора по умолчанию для my_tuple<>

constexpr my_tuple() {}
explicit my_tuple(const Types&...){} /* with empty pack Types */

в результате возникает ошибка.

При активации CTAD вы больше не генерируете проблемные my_tuple<> (которые, кстати, все еще проблематичны).

Я действительно удивлен :) Я знаю, что пакет параметров шаблона также может принимать нулевые аргументы, но я не ожидал, что при вызове обоих исходных конструкторов аргументы int и double будут правильно приняты. Например, вызов конструктора пары auto mtp2 = my_tuple(p); правильно вызывает template <class U1, class U2> my_tuple(const std::pair<U1, U2>& u) {} (я проверил, что могу напечатать оба значения внутри конструктора), но затем возвращается тип my_tuple<> без пары, без целого числа и без двойного значения. Как это возможно?

J L 16.08.2024 17:10
Types не выводится из этих конструкторов, поэтому пусто. std::tuple может сделать что-то вроде tuple(std::pair<Ts...>) requires (sizeof...(Ts) == 2).
Jarod42 16.08.2024 17:34

Другие вопросы по теме

Похожие вопросы

Может ли кто-нибудь объяснить правила времени жизни объектов и неинициализированной памяти в C++ (контекст: `std::inplace_vector`)?
Как вложенные векторы выглядят в памяти?
Почему перегрузка оператора предварительного приращения не вызывается для моего пользовательского класса итератора в C++?
Синтаксический сахар для синхронизации блока кода в C++
Как ограничить функцию шаблона определенными типами?
Visual Studio C++: копирование проектов и решений с одного компьютера на другой (github не работает)
Странное поведение кнопки EnableWindow при нажатии
Можно ли с уверенностью предположить, что 32-битные числа с плавающей запятой можно напрямую сравнивать друг с другом, если значение соответствует мантиссе?
Практическое руководство: функция C++, которая настраивает тип возвращаемого значения в соответствии с потребностями вызывающей стороны
Возвращает ли low_bound() один и тот же результат с обратными итераторами вектора в порядке возрастания и прямыми итераторами вектора в порядке убывания?