Назначенные инициализаторы C++20 с шаблонными типами

Как назначенные инициализаторы (C++20) должны работать с CTAD?

Этот код отлично работает в gcc9.2, но не работает с clang8

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{.first = 20, .second = 20.f};
    static_assert( std::is_same_v<decltype(x.first), int> );
    static_assert( std::is_same_v<decltype(x.second), float> );
}

Это должно быть действительно?

См. Пример на https://godbolt.org/z/KtNI43

1 ответ

Решение

Да, это должно быть действительно так.

Принцип работы CTAD заключается в том, что мы выполняем разрешение перегрузки над синтезированным набором конструкторов, чтобы выяснить, каковы были параметры шаблона класса. Начиная с C++17, этот синтезированный набор конструкторов основан только на конструкторах основного шаблона и руководствах по выводам (я меняю имена параметров шаблона, потому что они меня очень сбивают с толку):

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...>;

C++20 добавляет нового кандидата на совокупный вычет. Для каждого элемента списка -инициализатора или списка- назначенного-инициализатора мы выбираем соответствующий элемент агрегата и используем его тип в качестве нового кандидата. За

my_pair x{.first = 20, .second = 20.f};

Тип first является T и тип second является U, следовательно:

// aggregate deduction candidate
template <class T=int, class U=float>
auto __f(T, U) -> my_pair<T, U>;

Я написал эти четыре кандидата как функции (потому что мне легче думать о них как о функциях), но формулировка определяет их как конструкторы гипотетического типа класса. Итак, когда мы выполняем разрешение перегрузки, используя{.first = 20, .second = 20.f}, если прищуриться, вроде работает.

Последний кандидат является лучшим кандидатом (жизнеспособны только совокупный кандидат на вычет и руководство по вычету, совокупный кандидат на вычет более специализирован), поэтому мы получаем my_pair<int, float>.


Закончив CTAD, мы начинаем заново и эффективно делаем

my_pair<int, float> x{.first = 20, .second = 20.f};

Что, конечно, работает.

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