NTTP 的结构类型:为什么所有成员都必须是公共的?

Structural types as NTTP: why must all members be public?

本文关键字:类型 结构 为什么 NTTP 成员      更新时间:2023-10-16

在下面的示例中

template<auto V>
struct A {};
struct B {
constexpr B(int a) : value{a} {}
private:
int value{0};
};
int main() {
constexpr B b{0};
A<b> t2;        
}

成员value必须是公共的,才能使类型B结构化,才能将其用作A的 NTTP

。那么,没有具有私有成员的类型用作 NTTP? 这是 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1907r1.html 的真正意图吗?

这是[P1907]的真正意图吗?

P1907 的目的是正确指定两个值在模板参数上下文中等效的含义。在本文之前,等价的概念首先基于<=>(P0732(,然后基于==(P1185(,但是使用这些比较运算符作为基础存在很多问题(如论文中所述(。

考虑指针。我们已经有很长时间了写作的能力:

template <char const*> struct X { };

在这里,如果指针pq指向具有静态存储持续时间的同一对象或两个空指针,则X<p>X<q>相同的类型(P1907还允许两者都是过去结束的指针或两者都指向同一子对象(。

现在,考虑如下类型:

struct S {
char const* b;
char const* e;
char const* c;
};

如何:

template <S> struct Y { };

工作?此类型具有所有公共成员,因此可以肯定地说它没有不变量 - 任何人都可以修改这些指针中的任何一个以指向任何内容。规则是,如果s1.bs2.b是相同的指针,如果s1.es2.e是相同的指针,并且如果s1.cs2.c是相同的指针,则Y<s1>Y<s2>是同一类型。所有三个成对都是相同的。

现在考虑如下类型:

class String {
private:
char const* begin_;
char const* end_;
char const* capacity_;
public:
constexpr String();
constexpr String(char const*);
constexpr String(String const&);
constexpr String& operator=(String const&);
constexpr ~String();
};

此类型具有私有成员,因此强制执行一些不变量 - 并且可能对"相同"的含义有不同的理解,而不仅仅是"其所有成员都是成对等效的"。事实上,String大致是std::string的外观(忽略 SSO(,如果我们采用成员等效的方法,那么我们会遇到以下情况:

template <String> struct Z { };

Z<"hello"s>每次都可能给出不同的类型 - 因为我们必须分配存储来保存字符串,并且这些底层指针可能不同。P1907的一个不小的驱动因素是,最终允许:

template <std::string> struct C;

这样,C<"hello">不仅在翻译单元中始终是相同的类型,而且在所有翻译单元中也始终是相同的类型。基于<=>/==的机制不适用于这种情况。让std::string作为非类型模板参数正常工作的唯一方法是能够有一个自定义机制,其中std::string的作者指定等效性的基础——在这种情况下,我们甚至根本不会看容量指针,它只是基于从begin_end_的字符序列(而不是指针值, 指向的值(。

这种新机制尚不存在(作者暗示了operator template(,因此在它存在之前有两种选择:

  1. 删除所有类类型作为非类型模板参数
  2. 提出一种默认机制,该机制适用于某些类型,
  3. 但不会将不正确的等效概念添加到其他类型,即使这会阻止将某些类类型用作非类型模板参数,而成员等效规则是正确的。

我们选择了选项 2,使用"所有公共"作为执行简单、成员等效的机制......理解C++23将有这样的机制(如operator template(允许非类型模板参数,如std::stringstd::vector<char>std::optional<int>等。

答案很简单,但令人失望:C++20 不允许这样做。最终 c++23 将引入更广泛的解决方案......