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

wim*_*aan 2 c++ templates c++20

在下面的例子中

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;        
}
Run Code Online (Sandbox Code Playgroud)

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

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

Bar*_*rry 6

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

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

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

template <char const*> struct X { };
Run Code Online (Sandbox Code Playgroud)

而在这里,X<p>并且X<q>同一类型的,如果指针pq指向具有静态存储持续时间或两个空指针同一个对象(P1907也允许既要过去最末端的指针或两个相同的子对象的能力)。

现在,考虑一个类型:

struct S {
    char const* b;
    char const* e;
    char const* c;
};
Run Code Online (Sandbox Code Playgroud)

怎么会:

template <S> struct Y { };
Run Code Online (Sandbox Code Playgroud)

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

现在考虑一个类型:

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();
};
Run Code Online (Sandbox Code Playgroud)

这种类型有私有成员,因此,强制执行一些不变量 - 并且可能对“相同”必须意味着什么有不同的理解,而不是简单地“其所有成员都是成对等价的”。实际上,String大致是这样的std::string(忽略 SSO),如果我们采用成员等效的方法,那么我们会遇到以下情况:

template <String> struct Z { };
Run Code Online (Sandbox Code Playgroud)

Z<"hello"s>每次可能会给出不同的类型——因为我们必须分配存储空间来保存字符串,而那些底层指针可能会有所不同。P1907 的一个重要驱动因素是希望最终允许:

template <std::string> struct C;
Run Code Online (Sandbox Code Playgroud)

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

那个新机制还不存在(作者已经暗示了operator template),所以在它存在之前有两个选择:

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

我们选择了选项 2,使用“all public”作为进行简单的、按成员的等价的机制......并理解 C++23 将具有这样的机制(如operator template)以允许非类型模板参数类型喜欢std::stringstd::vector<char>std::optional<int>等。

  • @NicolBolas:“运算符模板”将以编译器理解的格式**识别**字符,并且可以破坏链接;用户编写的“operator==”会“检查”它们,但通常不能用于确定身份中需要包含的内容。 (3认同)