![](/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.