Назначенные инициализаторы 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};
Что, конечно, работает.