G++-11 销毁顺序从 G++9 更改
G++-11 destruction order changed from G++9
我们有以下代码(比较复杂,我试着做了一个最小的例子)。
#include <iostream>
#include <vector>
#include <string>
#include <memory>
template<typename T>
struct use_type
{
use_type(T& v) : value(v) {}
T& value;
};
template<typename T>
use_type<T> use(T& value) { return use_type<T>(value); }
template<typename T>
use_type<T> use(const T& value) { return use_type<T>(const_cast<T&>(value)); }
template<typename T>
struct printer_helper
{
void use(const use_type<T>& use) { uses.push_back(use); }
void final_action()
{
for (const auto& use : uses)
{
std::cout << use.value.size() << std::endl;
for (const auto& v : use.value)
{
std::cout << v << ",";
}
std::cout << std::endl;
}
}
std::vector<use_type<T>> uses;
};
template<typename T>
struct printer
{
printer() { helper = new printer_helper<T>(); }
printer<T>& operator , (const use_type<T>& t)
{
helper->use(t);
return *this;
}
~printer()
{
helper->final_action();
delete helper;
}
printer(const printer&) = delete;
printer& operator =(const printer&) = delete;
printer(printer&&) = default;
printer& operator =(printer&&) = default;
printer_helper<T>* helper;
};
template<typename T>
struct printer_creator
{
printer<T> operator << (const char*) { return {}; }
};
int main()
{
using vec = std::vector<std::string>;
{
/*vec v1{"abc", "bcd"};
vec v2{"new", "old", "real"};*/
printer_creator<vec> p;
p << "", use(vec{"abc", "bcd"}), use(vec{"new", "old", "real"});
//p << p, use(v1), use(v2);
}
}
此代码在 g++-11 之前的 g++ 上运行良好。对于 g++-11,输出中要么是段错误,要么是垃圾。是UB吗?如果是的话,也许你可以告诉我,哪个 gcc 更改使它失败并在可能的情况下引用标准?
实例:
临时向量在创建它们的行的末尾被破坏。 p
在其范围的末尾被破坏(即在向量之后)。由于 p
的析构函数使用对现在悬挂的向量的引用,因此您的代码具有未定义的行为。
如果它成功了,那纯粹是“运气”,向量析构函数使向量处于可用状态。
在
p << "", use(vec{"abc", "bcd"}), use(vec{"new", "old", "real"});
您创建了 3 个临时文件
printer<vec>
来自 p << ""
)
vec{"abc", "bcd"}
vec{"new", "old", "real"}
它们之间没有顺序(见下面的注释),因此它们可以按任何顺序创建。
只有从左到右的破坏顺序是正确的(所以从右到左构造),其他顺序将使用悬挂引用并且是UB。由于您没有订单保证...
注:
我们通过重载 operator &&
/operator ||
,
来避免短路
我们通过重载 operator,
失去了从左到右的评估顺序
我们有以下代码(比较复杂,我试着做了一个最小的例子)。
#include <iostream>
#include <vector>
#include <string>
#include <memory>
template<typename T>
struct use_type
{
use_type(T& v) : value(v) {}
T& value;
};
template<typename T>
use_type<T> use(T& value) { return use_type<T>(value); }
template<typename T>
use_type<T> use(const T& value) { return use_type<T>(const_cast<T&>(value)); }
template<typename T>
struct printer_helper
{
void use(const use_type<T>& use) { uses.push_back(use); }
void final_action()
{
for (const auto& use : uses)
{
std::cout << use.value.size() << std::endl;
for (const auto& v : use.value)
{
std::cout << v << ",";
}
std::cout << std::endl;
}
}
std::vector<use_type<T>> uses;
};
template<typename T>
struct printer
{
printer() { helper = new printer_helper<T>(); }
printer<T>& operator , (const use_type<T>& t)
{
helper->use(t);
return *this;
}
~printer()
{
helper->final_action();
delete helper;
}
printer(const printer&) = delete;
printer& operator =(const printer&) = delete;
printer(printer&&) = default;
printer& operator =(printer&&) = default;
printer_helper<T>* helper;
};
template<typename T>
struct printer_creator
{
printer<T> operator << (const char*) { return {}; }
};
int main()
{
using vec = std::vector<std::string>;
{
/*vec v1{"abc", "bcd"};
vec v2{"new", "old", "real"};*/
printer_creator<vec> p;
p << "", use(vec{"abc", "bcd"}), use(vec{"new", "old", "real"});
//p << p, use(v1), use(v2);
}
}
此代码在 g++-11 之前的 g++ 上运行良好。对于 g++-11,输出中要么是段错误,要么是垃圾。是UB吗?如果是的话,也许你可以告诉我,哪个 gcc 更改使它失败并在可能的情况下引用标准?
实例:
临时向量在创建它们的行的末尾被破坏。 p
在其范围的末尾被破坏(即在向量之后)。由于 p
的析构函数使用对现在悬挂的向量的引用,因此您的代码具有未定义的行为。
如果它成功了,那纯粹是“运气”,向量析构函数使向量处于可用状态。
在
p << "", use(vec{"abc", "bcd"}), use(vec{"new", "old", "real"});
您创建了 3 个临时文件
printer<vec>
来自p << ""
)vec{"abc", "bcd"}
vec{"new", "old", "real"}
它们之间没有顺序(见下面的注释),因此它们可以按任何顺序创建。
只有从左到右的破坏顺序是正确的(所以从右到左构造),其他顺序将使用悬挂引用并且是UB。由于您没有订单保证...
注:
我们通过重载 operator &&
/operator ||
,
来避免短路
我们通过重载 operator,