C ++ 20中的CTAD和指定的初始值设定项

kaw*_*ken 7 c++ designated-initializer c++20 ctad

我已经在这个问题中对使用指定的初始化程序的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> );
}
Run Code Online (Sandbox Code Playgroud)

即使我没有在指定的初始值设定项中给出显式信息,推论指南似乎也将推导类型first推导为。无论关键字(),推论指南显然只关心初始化程序中的顺序。演绎指南应该明智吗?还是应该有一个“指定演绎指南”?float.first.second

请参阅https://godbolt.org/z/cm6Yi7上的示例

Bar*_*rry 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...>;
Run Code Online (Sandbox Code Playgroud)

并且聚合推演候选基于我们提供的实际初始化列表指定初始化列表,而不是聚合的实际底层成员。我们的指定初始值设定项列表{.second = 20.f}这样我们的聚合扣除候选者变成:

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

模板参数总是来自主类模板,所以我们从那里引入默认模板参数。候选参数来自initializer-list,类型secondU

聚合推演候选是最好的候选(只有聚合推演候选和推演指南是可行的,聚合推演候选更专业),所以我们最终得到my_pair<int, float>


完成 CTAD 后,我们现在重新开始并有效地做

my_pair<int, float> x{.second = 20.f};
Run Code Online (Sandbox Code Playgroud)

哪个有效,并导致x.first{}.


用于聚合的 CTAD 直到最近才被采用(两个月前,在 2019 年 7 月的科隆会议上)。在该功能之前,这仍然是格式良好的:

my_pair{.second = 20.f};
Run Code Online (Sandbox Code Playgroud)

为什么?我们还没有汇总扣除候选人,但我们仍然有扣除指南......这可行的。它给了我们my_pair<float>。也就是说,my_pair<float, float>一旦你为U.

这就是为什么 gcc 给你你看到的行为 - 它只是还没有为聚合实现 CTAD,而是给你旧的行为。

  • 所以,针对帖子中的问题进行解答。“first”被推导为“float”是一个编译器错误。 (2认同)