支撑时的汇编误差。

发布于 2025-02-07 21:19:05 字数 682 浏览 1 评论 0 原文

当我试图支撑 std :: vector< foo> 的同事时,我的一位同事发现了与GCC的奇怪汇编错误提供了一个模板构造函数,例如在以下示例中:(

#include <vector>

struct Foo{
    template <class T> Foo(T) {}
    Foo(const Foo&) = delete;
    Foo(Foo&&) = default;
};

int main(){
    std::vector<Foo> v1(std::vector<Foo>{}); // ok
    std::vector<Foo> v2{std::vector<Foo>{}}; // compilation error
}

请参阅 Compiler Explorer 。)

IS这是一个错误吗?如果是这样,(我想在实例化过程中)在哪里犯错了?换句话说,由于所有三个编译器的代码都可以,一旦删除了模板的约束,后者的旅行gcc中会有什么?

A colleague of mine has find a strange compilation error with GCC when one tries to brace initialize a std::vector<Foo> with another, if Foo is move-only but provides a templated constructor, like in the following example:

#include <vector>

struct Foo{
    template <class T> Foo(T) {}
    Foo(const Foo&) = delete;
    Foo(Foo&&) = default;
};

int main(){
    std::vector<Foo> v1(std::vector<Foo>{}); // ok
    std::vector<Foo> v2{std::vector<Foo>{}}; // compilation error
}

(See code and error on Compiler Explorer.)

Is it a bug? If so, where (within the instantiation process, I guess) is GCC making the mistake? In other words, since the code is ok for all three compilers as soon as one removes the templated construtor, what in the latter trips GCC up?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

り繁华旳梦境 2025-02-14 21:19:05

如果我们停止使用向量,以便可以看到正在使用哪些构造函数,那么也许会更清楚地发生了什么。

template <typename T>
struct V {
    V() { std::cout << "V Default\n"; }
    V(const V&) { std::cout << "V Copy\n"; }
    V(V&&) { std::cout << "V Move\n"; }
    V(std::initializer_list<T>) { std::cout << "V list<" << typeid(T).name() << ">\n"; }
};

struct Foo {
    template <class T>
    Foo(T) {  std::cout << "Foo <" << typeid(T).name() << ">\n"; }
    Foo(const Foo&) = delete;
    Foo(Foo&&) { std::cout << "Foo Move\n"; }
};

https://godbolt.org/z/z/9qs9bvjjz

; foo&gt; 从 v&lt; foo&gt; 使用() {}

V<Foo> v0{};
V<Foo> v1(v0);
V<Foo> v2{v1};

要使用GCC和Clang创建 V1 ,我们获得了“ V复制”,请将v选择为最佳的V ctor。对于 v2 ,我们也会获得“ V复制”,但是对于GCC,我们获得了“ V list&lt; foo&gt;”。它使用V的初始化列表构造函数,而不是复制构造函数。

现在,应该想知道,我们如何从 v&lt; foo&gt; 的一个元素的列表中获取 std :: prinitizer_list&lt; foo&gt; ?这是因为 foo 的模板构造函数,它提供了转换构造函数,可以从 v&lt; foo&gt; 中创建 foo

海湾合作委员会的行为与我们写的一样:

V<Foo> v2( { Foo(v1) } );

我认为海湾合作委员会在这里是正确的。

v&lt; foo&gt; v2 {v1} 是直接列表initialization,分辨率分为两个阶段。第一个是仅考虑 std :: prinitizer_list 构造函数,然后第二个是将所有构造函数视为参数。第一阶段应通过将 v1 转换为 foo 来产生匹配,这就是GCC选择的。

如果尚不清楚,一旦选择了initializer_list构造函数,则您会出现矢量错误,因为不可能将构造函数与不可复制对象列表一起使用。 initializer_list的元素是const,并且向量从中被复制。

If we stop using vector so we can see what constructors are getting used, then maybe it gets more clear what's going on.

template <typename T>
struct V {
    V() { std::cout << "V Default\n"; }
    V(const V&) { std::cout << "V Copy\n"; }
    V(V&&) { std::cout << "V Move\n"; }
    V(std::initializer_list<T>) { std::cout << "V list<" << typeid(T).name() << ">\n"; }
};

struct Foo {
    template <class T>
    Foo(T) {  std::cout << "Foo <" << typeid(T).name() << ">\n"; }
    Foo(const Foo&) = delete;
    Foo(Foo&&) { std::cout << "Foo Move\n"; }
};

https://godbolt.org/z/9qs9bvjjz

Now let's try to copy construct a V<Foo> from a V<Foo>, using both () and {}.

V<Foo> v0{};
V<Foo> v1(v0);
V<Foo> v2{v1};

To create v1 with both gcc and clang, we get "V Copy", copy ctor of V chosen as best. For v2 on clang we get "V Copy" as well, but for gcc, we get "V list<Foo>". It's using the initializer list constructor of V instead of the copy constructor.

Now, one should wonder, how did we get a std::initializer_list<Foo> from a list of one element of V<Foo>? That's because of the template constructor of Foo, which provides a conversion constructor to allow creating a Foo from a V<Foo>.

gcc acts the same as if we had written:

V<Foo> v2( { Foo(v1) } );

I think gcc is correct here.

V<Foo> v2{v1} is direct-list-initialization and the resolution is done in two phases. The first is to consider only std::initializer_list constructors, then the second is to consider all constructors with the list as the arguments. The first phase should produce a match, by converting v1 into a Foo, and that's what gcc picks.

In case it's not clear, once the initializer_list constructor is chosen, you get an error with vector because it's not possible to use that constructor with a list of non-copyable objects. The initializer_list's elements are const and the vector is copy-initialized from it.

极度宠爱 2025-02-14 21:19:05

是一个错误吗?

我认为, GCC在拒绝程序的情况下是正确的:

std :: vector 具有构造函数 采用 std :: pritializer_list

vector (initializer_list<value_type> il, const allocator_type& alloc = allocator_type());

code :当您写作时:

std::vector<Foo> v2{std::vector<Foo>{}};

在上述语句中,引用的初始化器列表ctor ctor vector 可以使用/使用。

此外,复制构造函数 foo :: foo(const foo&amp;)将在此处使用,因为来自 dcl.init.lst

类型的对象 std :: prinitizer_list&lt; e&gt; 是从初始化器列表中构造的const e ,其中 n 是初始化器列表中的元素数量。 该数组的每个元素都用初始化器列表的相应元素进行复制定义,并且构建了std :: prinistizer_list对象,以引用该数组。

(强调矿山)

但是,由于复制构造函数为已删除,我们会得到上述错误。您可以通过默认复制ctor而不是按照来确认确实是这种情况。 “>此演示,我们可以注意到那里 nockiles(compiles)问题。

Is it a bug?

I think that GCC is correct in rejecting the program for the reasons described below:

std::vector has a constructor that takes an std::initializer_list:

vector (initializer_list<value_type> il, const allocator_type& alloc = allocator_type());

Thus when you wrote:

std::vector<Foo> v2{std::vector<Foo>{}};

In the above statement, the quoted initializer list ctor of vector can be used/utilised.

Moreover, the copy constructor Foo::Foo(const Foo&) will be used here since from dcl.init.lst:

An object of type std::initializer_list<E> is constructed from an initializer list as if the implementation allocated a temporary array of N elements of type const E, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list object is constructed to refer to that array.

(emphasis mine)

But since the copy constructor is deleted we get the mentioned error. You can confirm that this is indeed the case by defaulting the copy ctor instead of deleting it as done in this demo and as we can notice there the program works(compiles) without any issues.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文