gpt4 book ai didi

c++ - 有一个空对基类的目的是什么?

转载 作者:行者123 更新时间:2023-12-03 07:26:31 29 4
gpt4 key购买 nike

libstdc++对 pair 的实现有以下奇怪之处

template<typename, typename> class __pair_base
{
template<typename T, typename U> friend struct pair;
__pair_base() = default;
~__pair_base() = default;
__pair_base(const __pair_base&) = default;
__pair_base& operator=(const __pair_base&) = delete;
};

template<typename T, typename U>
struct pair
: private __pair_base<T, U>
{ /* never uses __pair_base */ };
__pair_base考虑到它是空的,从未使用过,也不能使用。这尤其令人困惑,因为 std::pairrequired有条件的结构

pair<T, U> is a structural type if T and U are both structural types.


拥有私有(private)基地使其成为非结构性的。

最佳答案

tl;dr 这是为了实现 std::pair 的疯狂过载/显式规则而进行的一系列非常长的黑客攻击的结果。并保持 ABI 兼容性。这是 C++20 中的一个错误。

免责声明
这更像是一个“有趣”的旅程,与标准库作者一起沿着内存之路走下去,然后是一些有见地的语言层面的启示。它显示了 C++ 变得多么复杂,实现一对,在所有事情中,是一项艰巨的任务。
我尽力重现历史,但我不是作者之一。
对底漆std::pair不仅仅是简单的

template<typename T, typename U>
struct pair
{
T first;
U second;
};
cppreference 上列出了 8 个不同的构造函数,对于一个实现者来说,它甚至更多:每个条件显式构造函数实际上是两个构造函数,一个用于隐式,另一个用于显式。
并非所有这些构造函数都参与重载决议,如果他们参与,那么到处都会有歧义。取而代之的是,有许多规则来管理每个规则,并且上述案例的每种组合都必须由 SFINAE 手动编写和禁用。
多年来,仅针对构造函数的错误报告就达到了顶峰。现在快变成 6 了;)
序幕
first bug如果类型相同,则有关短路对参数的可转换性检查。
template<typename T> struct B;
template<typename T> struct A
{
A(A&&) = default;
A(const B<T> &);
};

template<typename T> struct B
{
pair<A<T>, int> a;
B(B&&) = default;
};
显然,如果他们过早检查可转换性,移动构造函数会由于循环依赖以及 B 的方式而被删除。 A 内仍不完整. nonesuch然而这 changed the SFINAE propertiespair .作为回应,实现了另一个修复程序。此实现启用了以前无效的赋值运算符,因此通过更改其签名手动关闭了赋值运算符
struct nonesuch
{
nonesuch() = delete;
~nonesuch() = delete;
nonesuch(nonesuch const&) = delete;
void operator=(nonesuch const&) = delete;
};

// ...
pair& operator=(
conditional_t<conjunction_v<is_copy_assignable<T>,
is_copy_assignable<U>>,
const pair&, const nonesuch&>::type)
在哪里 nonesuch是一个虚拟类型,本质上使这个重载不可调用。或者是吗? no_braces_nonesuch不幸的是,即使您永远无法创建 nonesuch
pair<int, int> p = {};  // succeeds
p = {}; // fails
你仍然可以 initialize it with braces .由于 delete不能解决重载决议,这是一个硬故障。
解决方法是创建 no_braces_nonesuch
struct no_braces_nonesuch : nonesuch
{
explicit no_braces_nonesuch(const no_braces_nonesuch&) = delete;
};
explicit关闭参与重载决议。最后,分配是不可调用的。还是……? __pair_base v1
不幸的是, another way to initialize未知类型
struct anything
{
template<typename T>
operator T() { return {}; }
};

anything a;
pair<int, int> p;
p = a;
作者意识到他们可以通过利用默认生成的特殊成员函数“轻松”解决这个问题:如果你有一个不可赋值的基数,它们根本就不会被声明
class __pair_base
{
template<typename T, typename U> friend struct pair;
__pair_base() = default;
~__pair_base() = default;
__pair_base(const __pair_base&) = default;
__pair_base& operator=(const __pair_base&) = delete;
};
所有单元测试都通过了,事情看起来很光明。不知不觉中,一只邪恶 bug 的影子不祥地隐约出现在地平线上。 __pair_base v2
ABI 坏了。
这怎么可能? Empty bases are optimized他们不是吗?嗯,不。
pair<pair<int, int>, int> p;
不幸的是,空基优化仅适用于基类子对象与相同类型的其他子对象不重叠的情况。在这种情况下, __pair_base内对中的一个与外对中的一个重叠。
修复很“简单”,我们将 __pair_base 模板化以确保它们是不同的类型。
结构类型
C++20 来了,它要求对是 structural types .这要求没有私有(private)基地。
template<pair<int, int>>
struct S; // fails
就这样结束了我们的旅程。这让我想起了 Chandler Carruth's quick survey at cppcon :“如果需要,谁能在一年内构建出 C++ 编译器?”鉴于 C++ 的复杂性,只有当前的编译器编写者认为他们可以。显然,我什至不知道如何实现 std::pair .

关于c++ - 有一个空对基类的目的是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64931324/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com