Mq-b/Modern-Cpp-templates-tutorial

`requires` 表达式中“类型要求”

Closed this issue · 15 comments

Mq-b commented

众所周知,requires 表达式如果要使用类型要求,得在要求序列中写 typename,但是为何以下代码不需要,且结果正确,那它是什么?

#include <iostream>

template<typename T>
void f(T){
    constexpr bool v = requires{ T::type; };
    std::cout << std::boolalpha << v << '\n';
}

struct X { using type = void; };

int main(){
    f(1);   // false 因为 int::type 不是合法表达式
    f(X{}); // true 因为 X::type 是合法表达式
}

测试可通过编译。

但是如果这个 requires 表达式是用作定义 concept 的时候,不使用 typename 就有不符合预料的结果:

#include <iostream>

template<typename T>
concept C = requires{ T::type; };

struct X { using type = int; };

int main(){
    std::cout << std::boolalpha << C<int> << '\n'; // false
    std::cout << std::boolalpha << C<X> << '\n';   // false
}

测试
如果我们给概念(concept)C 的 requires 表达式中的 T::type 改成 typename T::type,那么运行结果就会变成:falsetrue测试

会不会是因为 T::type本身就是不对的,
$@8X4666LBKCIZBP%DGUS40

D22WMCUXIGU QI0QDQ0$2EA

typename T::type才是对的, 图来自cppinsight

Mq-b commented

不好说,第一个示例实测了是三个编译器都能通过编译并且得到正确的结果,难道大家都错了?这其实是个扩展?

不加typename,编译器不会把type当成类型处理, https://gcc.godbolt.org/z/TGM9fMErr

Mq-b commented

简单,那第一个用法应该是合理的,它只是普通的 requires 表达式而已,不是类型要求

我觉得两种都是对的,只是一个要求类中有某个静态成员,另一个是要求类中有某种类型

Mq-b commented

不不不,第一个只是要求满足这种形式,是合法表达式即可,而不是类型要求。

Mq-b commented

第一个应该算作是“简单要求”。

简单要求是任何不以关键词 requires 开始的表达式语句。它断言该表达式是有效的。表达式是不求值的操作数;只检查语言的正确性。

Mq-b commented

何不以关键词 requires 开始的表达式语句

这句话是表达啥?我知道“简单要求”是指啥,但是这句话指啥不知道。

https://zh.cppreference.com/w/cpp/language/dependent_name
在模板(包括别名模版)的声明或定义中,不是当前实例化的成员且取决于某个模板形参的名字不会被认为是类型,除非使用关键词 typename 或它已经被设立为类型名(例如用 typedef 声明或通过用作基类名)
所以编译器觉得你是要找一个叫type的成员,没有就false了

Mq-b commented

https://zh.cppreference.com/w/cpp/language/dependent_name 在模板(包括别名模版)的声明或定义中,不是当前实例化的成员且取决于某个模板形参的名字不会被认为是类型,除非使用关键词 typename 或它已经被设立为类型名(例如用 typedef 声明或通过用作基类名)。 所以编译器觉得你是要找一个叫type的成员,没有就false了

这是待决名常识,我知道,我就从来没纠结过它会不会被判断为类型,只是前面在想这个语法形式

第一个情况是良构的,只是 requires 表达式求值为 false

达成我们期待的写法应该是 requires { T::value; }requires { typename T::type; },但是 typevalue 并不是关键词,所以用混了也不会直接导致非良构,只是产生不期待的结果(除非在 static_assert 里或导致重载决议失败等等)。

Mq-b commented

聚聚可以解释一下这个嘛?

任何不以关键词 requires 开始的表达式语句

这句话是表达啥?我知道“简单要求”是指啥,但是这句话指啥不知道。

聚聚可以解释一下这个嘛?

任何不以关键词 requires 开始的表达式语句

这句话是表达啥?我知道“简单要求”是指啥,但是这句话指啥不知道。

标准文档原文:

A requirement that starts with a requires token is never interpreted as a simple-requirement.
[Note 2: This simplifies distinguishing between a simple-requirement and a nested-requirement.
— end note]

翻译:

以requires关键词开始的要求永远不会被解释为简单要求
注2: 这简化了简单要求嵌套要求之间的区分。 

因吹斯听,又学到了新知识了,又是美好的一天

聚聚可以解释一下这个嘛?

任何不以关键词 requires 开始的表达式语句

这句话是表达啥?我知道“简单要求”是指啥,但是这句话指啥不知道。

标准文档原文:

A requirement that starts with a requires token is never interpreted as a simple-requirement. [Note 2: This simplifies distinguishing between a simple-requirement and a nested-requirement. — end note]

翻译:

以requires关键词开始的要求永远不会被解释为简单要求。 注2: 这简化了简单要求嵌套要求之间的区分。 

因吹斯听,又学到了新知识了,又是美好的一天

这其实是同一个意思。我们需要语文水平好的人把它说得通顺点(英文原文也很绕)。