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

str*_*ger 16 c++ language-lawyer c++17

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

换句话说,我知道以下语句是有效的:

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};
Run Code Online (Sandbox Code Playgroud)

但是以下陈述也有效吗?

std::initializer_list x1{1, 2, 3};
std::initializer_list x2 = {1, 2, 3};
auto x3 = std::initializer_list{1, 2, 3};
Run Code Online (Sandbox Code Playgroud)

编译器不同意是否std::initializer_list可以推导的模板参数:

#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
}
Run Code Online (Sandbox Code Playgroud)

(请参阅Compiler Explorer上的示例。)

编译器经过测试:

  • 带有-std=c++17 -stdlib=libc++和的Clang版本7.0.0-std=c++17 -stdlib=libstdc++
  • GCC版本8.3与 -std=c++17
  • MSVC版本19.16与 /std:c++17

Nic*_*las 9

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

当编译器看到没有模板参数的模板名称时,它必须查看模板的推导指南,并将其应用于braced-init-list中的参数。initializer_list没有任何明确的推论指南,因此它使用了可用的构造函数。

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

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

  • 我不相信。您将应用[over.match.class.deduct],它除其他外还形成了来自复制推演候选者的功能模板。该函数模板看起来像`template &lt;class T&gt; auto X(std :: initializer_list &lt;T&gt;)-&gt; std :: initializer_list &lt;T&gt;;`然后[over.match.class.deduct]说去[over.match.list]使用提供的初始化器{{1,2,3}`将函数模板视为正在初始化的某些假设类类型的构造函数模板。如果这样做,您将得出T = int。 (3认同)