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 для агрегатов и дает вам старое поведение.

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