繁体   English   中英

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

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

这个问题是在试验从std::tuple派生的 class 以重用元组的比较运算符时开始的。 下面的代码应该从 2D 或 3D 坐标列表中删除重复项。

在主 function 的开头,第 49 和 50 行,有两个向量v2v3用初始化列表初始化。 我怀疑初始化有问题,但不知道是什么。

g++ 在优化级别 {0, 1, 2} 下产生错误结果,在优化级别 3 下产生正确结果。

clang++ 在优化级别 {1, 2, 3} 下产生错误结果,在优化级别 0 下产生正确结果。

问题:错误的结果是由于编译器错误还是我的代码中有一些未定义的行为?

在 Ubuntu 20.10 上运行的编译器版本是:

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

正确的 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) 

不正确的 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) 

编译命令示例:

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

代码(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";
}

问题在于复制ctor。 由于不存在用户提供的复制ctor,因此会生成编译器默认值; 这从不再有效的来源复制参考。 您需要复制/移动明确初始化这些引用的 ctor:

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

如果需要,也可以定义移动 ctor。 类似的论点也适用于Point3D 您的代码目前在这方面触发了 UB。

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

PS:不要错过作业:

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

同样,可以类似地定义移动分配。

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM