CTAD и назначенные инициализаторы в C++20
В этом вопросе я уже говорил о путанице по поводу CTAD с назначенными инициализаторами, но у меня есть еще одна путаница с очень похожим фрагментом кода
template <typename int_t=int, typename float_t=float>
struct my_pair {
int_t first;
float_t second;
};
template<typename ... ts>
my_pair(ts...) -> my_pair<ts...>;
int main() {
my_pair x{.second = 20.f};
static_assert( std::is_same_v<decltype(x.first), int> ); //FAILS <- its deduced to float
static_assert( std::is_same_v<decltype(x.second), float> );
}
Похоже, что руководство по дедукции вызывает тип first
быть выведенным к float
, хотя я не даю явного .first
в назначенном инициализаторе. Руководство по выводам, по-видимому, заботится только о порядке в инициализаторе, независимо от ключевого слова (.second
). Должно ли руководство по дедукции быть умным в этом или должно быть "специальное руководство по дедукции"?
См. Пример на https://godbolt.org/z/cm6Yi7
1 ответ
Рассмотрите этот ответ как отправную точку. У нас есть те же первые три кандидата:
template <class T=int, class U=float>
struct my_pair {
T first;
U second;
};
// default constructor
template <class T=int, class U=float>
auto __f() -> my_pair<T, U>;
// copy candidate
template <class T=int, class U=float>
auto __f(my_pair<T, U>) -> my_pair<T, U>;
// deduction guide
template <class... T>
auto __f(T...) -> my_pair<T...>;
И кандидат на вычет агрегата основан на фактическом списке инициализаторов или списке назначенных инициализаторов, который мы предоставляем, а не на фактических базовых элементах агрегата. Наш назначенный-инициализатор-лист является{.second = 20.f}
Итак, наш совокупный кандидат на вычет становится:
// aggregate deduction candidate
template <class T=int, class U=float>
auto __f(U) -> my_pair<T, U>;
Параметры шаблона всегда берутся из шаблона основного класса, поэтому мы вводим аргументы шаблона по умолчанию оттуда. Аргументы-кандидаты берутся из списка инициализаторов, а типsecond
является U
.
Кандидат на совокупный вычет - лучший кандидат (жизнеспособны только кандидат на совокупный вычет и руководство по вычету, кандидат на совокупный вычет более специализирован), поэтому мы получаем my_pair<int, float>
.
Закончив CTAD, мы начинаем заново и эффективно делаем
my_pair<int, float> x{.second = 20.f};
Что работает и приводит к x.first
инициализируется из {}
.
CTAD для агрегатов был принят совсем недавно (на встрече в Кельне в июле 2019 года, два месяца назад). До этой функции это все еще было бы правильно сформировано:
my_pair{.second = 20.f};
Зачем? Мы пока не имеем совокупный кандидата дедукции, но мы до сих пор есть вычет руководство... который является жизнеспособным. Это дает намmy_pair<float>
. То есть,my_pair<float, float>
после того, как вы введете аргумент шаблона по умолчанию для U
.
Вот почему gcc дает вам поведение, которое вы видите - он просто еще не реализует CTAD для агрегатов и дает вам старое поведение.