简体   繁体   English

g++ 和 clang++ 在列表初始化时给出不同的结果

[英]g++ and clang++ give different results on list initialization

This issue got started while experimenting on a class derived from std::tuple in order to reuse tuple's comparison operators.这个问题是在试验从std::tuple派生的 class 以重用元组的比较运算符时开始的。 The code below is supposed to remove duplicates from lists of 2D or 3D coordinates.下面的代码应该从 2D 或 3D 坐标列表中删除重复项。

In the beginning of the main function, lines 49 and 50, there are two vectors v2 and v3 initialized with initializer lists.在主 function 的开头,第 49 和 50 行,有两个向量v2v3用初始化列表初始化。 I suspect that there is something wrong in the initialization but can not figure out what.我怀疑初始化有问题,但不知道是什么。

g++ produces wrong results with optimization levels {0, 1, 2} and correct results with optimization level 3. g++ 在优化级别 {0, 1, 2} 下产生错误结果,在优化级别 3 下产生正确结果。

clang++ produces wrong results with optimization levels {1, 2, 3} and correct results with optimization level 0. clang++ 在优化级别 {1, 2, 3} 下产生错误结果,在优化级别 0 下产生正确结果。

Question: Are the wrong results due to compiler bugs or is there some undefined behavior in my code?问题:错误的结果是由于编译器错误还是我的代码中有一些未定义的行为?

The compiler versions running on Ubuntu 20.10 are:在 Ubuntu 20.10 上运行的编译器版本是:

g++ (Ubuntu 10.2.0-13ubuntu1) 10.2.0
Ubuntu clang version 11.0.0-2

The correct output is:正确的 output 是:

Before duplicate removal:
(1,2) (2,3) (1,2) (4,5) (1,2) 
(1,2,3) (4,5,6) (1,2,3) (7,8,9) (1,2,3) 
After duplicate removal:
(1,2) (2,3) (4,5) 
(1,2,3) (4,5,6) (7,8,9) 

The incorrect output is:不正确的 output 是:

Before duplicate removal:
(1074266112,0) (32764,-976103536) (1075314688,0) (32764,-976103488) (1074266112,0) 
(1,2,3) (4,5,6) (1,2,3) (7,8,9) (1,2,3) 
After duplicate removal:
(1074266112,0) (32764,-976103536) (32764,-976103488) 
(1,2,3) (4,5,6) (7,8,9) 

Examples of compilation commands:编译命令示例:

g++ point.cc -std=c++17 -O3
clang++ point.cc -std=c++17 -O0

The code (point.cc):代码(point.cc):

#include <iostream>
#include <vector>
#include <type_traits>
#include <tuple>
#include <utility>
#include <functional>
#include <algorithm>

template<class T, class W = std::reference_wrapper<T>>
struct Ref : W
{
    using W::W;
    Ref& operator=(const T& x) noexcept
    {
        this->get() = x;
        return *this;
    }
};

template<class T, class P = std::tuple<T,T>>
struct Point2D : P
{
    using P::P;
    // Make aliases for the tuple elements
    Ref<T> x = std::get<0>(*this);
    Ref<T> y = std::get<1>(*this);
};


template<class T, class P = std::tuple<T,T,T>>
struct Point3D : P
{
    using P::P;
    // Make aliases for the tuple elements
    Ref<T> x = std::get<0>(*this);
    Ref<T> y = std::get<1>(*this);
    Ref<T> z = std::get<2>(*this);
};

template <class V>
void removeDuplicates(V& v)
{
    std::sort(v.begin(), v.end());
    auto last = std::unique(v.begin(), v.end());
    v.erase(last, v.end());
}

int main() {
    std::vector<Point2D<int>> v2 { {1,2}, {2,3}, {1,2}, {4,5}, {1,2} };
    std::vector<Point3D<double>> v3 { {1,2,3}, {4,5,6}, {1,2,3}, {7,8,9}, {1,2,3} };

    std::cout << "Before duplicate removal:\n";
    for (auto p : v2)
      std::cout << "(" << p.x << "," << p.y << ") ";
    std::cout << "\n";

    for (auto p : v3)
      std::cout << "(" << p.x << "," << p.y << "," << p.z << ") ";
    std::cout << "\n";

    removeDuplicates(v2);
    removeDuplicates(v3);

    std::cout << "After duplicate removal:\n";
    for (auto p : v2)
      std::cout << "(" << p.x << "," << p.y << ") ";
    std::cout << "\n";

    for (auto p : v3)
      std::cout << "(" << p.x << "," << p.y << "," << p.z << ") ";
    std::cout << "\n";
}

The problem is with copy ctor.问题在于复制ctor。 Since user provided copy ctor is absent, compiler default is generated;由于不存在用户提供的复制ctor,因此会生成编译器默认值; this copies references from sources that are no more valid.这从不再有效的来源复制参考。 You need copy/move ctors that explicitly initialize those references:您需要复制/移动明确初始化这些引用的 ctor:

Point2D::Point2D(Point2D const& p)
     : P{p}
     , x{get<0>(*this)}
     , y{get<1>(*this)}
{};

The move ctor can as well be defined if needed.如果需要,也可以定义移动 ctor。 Similar argument holds for the Point3D too.类似的论点也适用于Point3D Your code currently triggers UB in that regard.您的代码目前在这方面触发了 UB。

============================================= ==============================================

PS: Do not miss the assignment: PS:不要错过作业:

auto& PointXD:: operator (PointXD& p){
    P&{*this}=p;
    return *this;//skip the refs; they're good
};

Again the move-assignment can be defined similarly.同样,可以类似地定义移动分配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 g ++和clang ++使用静态成员的递归初始化的不同行为 - g++ and clang++ different behaviour with recursive initialization of a static member C++20 成员初始化列表 Clang++ vs G++ - C++20 Member Initialization List Clang++ vs G++ 空包装的Functor可变参数模板包装扩展在clang ++和g ++中给出了不同的结果 - Functor variadic template pack expansion for empty pack gives different results in clang++ and g++ 这是 g++ 或 clang++ 中的错误 - Is this a bug in g++ or clang++ g ++和clang ++使用变量模板和SFINAE的不同行为 - g++ and clang++ different behaviour with variable template and SFINAE g ++和clang ++使用运算符&lt;()重载的不同行为 - g++ and clang++ different behaviour with operator<() overloading g ++和clang ++使用指向可变参数模板函数的指针的不同行为 - g++ and clang++ different behaviour with pointer to variadic template functions g ++和clang ++使用自动参数的模板特化的不同行为 - g++ and clang++ different behaviour with template specialization for auto argument g++ 和 clang++ 与可变参数容器的不同行为 - g++ and clang++ different behaviour with variadic container 带有集成模板参数的g ++和clang ++不同的行为 - g++ and clang++ different behaviour with integral template parameter
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM