首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >std::initializer_list{x,y,z} (CTAD)有效吗?

std::initializer_list{x,y,z} (CTAD)有效吗?
EN

Stack Overflow用户
提问于 2019-03-17 08:25:41
回答 2查看 426关注 0票数 19

在显式构造std::initializer_list<U>时,是否可以推导模板参数(例如,使用类模板参数演绎(CTAD) )?

换言之,我知道以下声明是有效的:

代码语言:javascript
运行
复制
std::initializer_list<int> x1{1, 2, 3};
std::initializer_list<int> x2 = {1, 2, 3};
auto x3 = std::initializer_list<int>{1, 2, 3};

但以下陈述是否也有效?

代码语言:javascript
运行
复制
std::initializer_list x1{1, 2, 3};
std::initializer_list x2 = {1, 2, 3};
auto x3 = std::initializer_list{1, 2, 3};

编译器对于是否可以推断出std::initializer_list的模板参数存在分歧:

代码语言:javascript
运行
复制
#include <initializer_list>

struct s {
    s(std::initializer_list<int>);
};

void f() {
    std::initializer_list x1{1, 2, 3};         // Clang ERROR; GCC OK;    MSVC OK
    std::initializer_list x2 = {1, 2, 3};      // Clang ERROR; GCC OK;    MSVC OK
    auto x3 = std::initializer_list{1, 2, 3};  // Clang ERROR; GCC OK;    MSVC OK

    s x4(std::initializer_list{1, 2, 3});      // Clang ERROR; GCC ERROR; MSVC OK
    s x5{std::initializer_list{1, 2, 3}};      // Clang ERROR; GCC OK;    MSVC OK
    s x6 = s(std::initializer_list{1, 2, 3});  // Clang ERROR; GCC OK;    MSVC OK
    s x7 = s{std::initializer_list{1, 2, 3}};  // Clang ERROR; GCC OK;    MSVC OK
    s x8 = std::initializer_list{1, 2, 3};     // Clang ERROR; GCC OK;    MSVC OK

    void g(std::initializer_list<int>);
    g(std::initializer_list{1, 2, 3});         // Clang ERROR; GCC OK;    MSVC OK
}

(请参阅编译器资源管理器上的示例。)

经测试的编者:

  • 使用-std=c++17 -stdlib=libc++-std=c++17 -stdlib=libstdc++的Clang版本7.0.0
  • GCC版本8.3与-std=c++17
  • 使用/std:c++17的MSVC版本19.16
EN

回答 2

Stack Overflow用户

发布于 2019-03-17 13:43:38

Clang是唯一正确的编译器。是的,真的。

当编译器看到没有模板参数的模板名时,它必须查看模板的演绎指南,并将它们应用到大括号列表中的参数中。initializer_list没有任何显式的演绎指南,所以它使用可用的构造函数。

initializer_list拥有的唯一可公开访问的构造函数是它的复制/移动构造函数和它的默认构造函数。从大括号中的列表创建std::initializer_list不是通过可公开访问的构造函数完成的。它是通过列表初始化完成的,这是一个只编译的进程。只有编译器才能执行构建一个步骤所需的顺序

考虑到所有这些,应该不可能在initializer_lists上使用CTAD,除非您正在从现有列表中进行复制。最后一部分可能是其他编译器在某些情况下如何使它工作的。在演绎方面,它们可以将大括号中的列表推演为initializer_list<T>本身,而不是应用[over.match.list]的一系列参数,因此演绎指南会看到副本操作。

票数 10
EN

Stack Overflow用户

发布于 2020-08-15 14:08:27

这是一个Clang错误,在Nicol Bolas的“永不改变的答案”的评论中得出结论。总之,与任何类类型一样,std::initializer_list有一个编译器提供了演绎指南[over.match.class.deduct]第1.3节

从假设的构造函数C(C)派生出一个额外的函数模板,称为复制演绎候选。

这意味着std::initializer_list具有编译器隐式声明的演绎指南:

代码语言:javascript
运行
复制
template <class T>
initializer_list (initializer_list <T>) -> initializer_list <T>;

当使用非空的初始化程序列表参数来推断此演绎指南的T时,标准的下列规则被应用到[temp.deduct.call]§1

如果从P中删除引用和cv-限定符给出了一些P‘和N的std::初始化器_ list 或P‘n,并且参数是一个非空的初始化程序列表(dcl.init.list),那么将独立地对初始化程序列表的每个元素执行推导,并将P’作为单独的函数模板参数类型P‘i和ith初始化器元素作为相应的参数。

因此,应该将T推导为int,这样类模板参数推导就会成功。

免责声明:我不是部族的拥护者..。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55205176

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档