![](/img/trans.png)
[英]g++ and clang++ different behaviour with recursive initialization of a static member
[英]g++ and clang++ give different results on list initialization
這個問題是在試驗從std::tuple
派生的 class 以重用元組的比較運算符時開始的。 下面的代碼應該從 2D 或 3D 坐標列表中刪除重復項。
在主 function 的開頭,第 49 和 50 行,有兩個向量v2
和v3
用初始化列表初始化。 我懷疑初始化有問題,但不知道是什么。
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.