簡體   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