10

我已经在这个问题中用指定的初始化程序对 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上的示例

4

1 回答 1

5

将此答案视为起点。我们有相同的初始三个候选人:

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

And the aggregate deduction candidate is based on the actual initializer-list or designated-initializer-list we provide, not the actual underlying members of the aggregate. Our designated-initializer-list is {.second = 20.f} so our aggregate deduction candidate becomes:

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

The template parameters always come from the primary class template, so we bring in the default template arguments from there. The candidate arguments come from the initializer-list, and the type of second is U.

The aggregate deduction candidate is the best candidate (only the aggregate deduction candidate and the deduction guide are viable, the aggregate deduction candidate is more specialized), so we end up with my_pair<int, float>.


Having finished CTAD, we now start over and effectively do

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

Which works, and leads to x.first being initialized from {}.


CTAD for aggregates was only adopted very recently (at the Cologne meeting in July 2019, two months ago). Before that feature, this would still have been well-formed:

my_pair{.second = 20.f};

Why? We don't yet have the aggregate deduction candidate, but we still do have the deduction guide... which is viable. It gives us my_pair<float>. Which is to say, my_pair<float, float> once you fill in the default template argument for U.

That's why gcc is giving you the behavior you see - it simply does not yet implement CTAD for aggregates, and is giving you the old behavior.

于 2019-09-11T12:34:31.863 回答