C++ 为什么与概念相同的_检查类型相等两次?

C++ 为什么与概念相同的_检查类型相等两次?,c++,language-lawyer,c++20,concept,C++,Language Lawyer,C++20,Concept,在查看同一概念的可能实现时,我注意到一些奇怪的事情正在发生 namespace detail { template< class T, class U > concept SameHelper = std::is_same_v<T, U>; } template< class T, class U > concept same_as = detail::SameHelper<T, U> && detail::Sam

在查看同一概念的可能实现时,我注意到一些奇怪的事情正在发生

namespace detail {
    template< class T, class U >
    concept SameHelper = std::is_same_v<T, U>;
}

template< class T, class U >
concept same_as = detail::SameHelper<T, U> && detail::SameHelper<U, T>;
名称空间详细信息{
模板
概念SameHelper=std::是否相同;
}
模板
概念与=细节::SameHelper和&细节::SameHelper;
第一个问题是为什么需要一个
SameHelper
概念?
第二个是为什么
same\U as
检查
T
是否与
U
相同,以及
U
是否与
T
相同?它不是多余的吗?

std::is_same
定义为true当且仅当:

T和U使用相同的简历资格命名相同的类型

据我所知,标准并没有定义“相同类型”的含义,但在自然语言和逻辑中,“相同”是一种等价关系,因此是可交换的

鉴于我所认为的这个假设,
是相同的&&is\u相同的
确实是多余的。但是,
相同的
没有在
相同的
方面指定;那只是为了说明

对两者的显式检查允许实现
与impl
相同,以满足
相同的要求,而无需交换。以这种方式指定它可以准确地描述概念的行为,而不限制它的实现方式


我不知道为什么选择这种方法而不是用
来说明是什么原因。所选方法的一个优点可以说是这两个定义是解耦的。一个不依赖另一个。

有趣的问题。我最近观看了Andrew Sutton关于概念的演讲,在问答环节中,有人问了以下问题(以下链接中的时间戳):

因此,问题可以归结为:
如果我有一个说a&&B&&C的概念,另一个说C&&B&&a,那么它们是等价的吗?
Andrew回答是的,但指出编译器有一些内部方法(对用户来说是透明的)来将概念分解成原子逻辑命题(
atomic constraints
,如Andrew所述)并检查它们是否等效

现在看看cppreference对
std::与
相同的内容:

// old impl.; was named Same back then
template<typename T, typename U>
concept same_as = is_same_v<T, U>;
std::same_as
包含
std::same_as
,反之亦然

这基本上是一种“当且仅当”的关系:它们相互暗示。(逻辑等价)

我猜想这里的原子约束是
std::is_same_v
。编译器对待
std::is_same_v
的方式可能会让他们认为
std::is_same_v
std::is_v
是两个不同的约束(它们是不同的实体!)。因此,如果您仅使用其中一个实现与
相同的std::u:

模板
概念相同=细节::SameHelper;
然后
std::same_as
std::same_as
将“爆炸”到不同的原子约束,并变得不等价

那么,为什么编译器会在意呢

考虑:

#包括
#包括
#包括
模板
概念SameHelper=std::是否相同;
模板
概念my_same_as=SameHelper;
//模板
//概念my_same_as=SameHelper&&SameHelper;
模板要求我的\U与
无效foo(T a,U b){

std::cout[concept.same]作为的一部分被更改(在概念
same
被重命名为
is_same
之前,根据)[emphasismine]:

3182.同样的规格可能更清晰
  • 第18.4.2节[概念相同]
  • 状态:WP
  • [……]
讨论:

18.4.2[概念.相同]中相同概念的说明:

调用,比如说,
f()
,并不含糊(
#1
将被调用),因为
#1
C1&&C2
处的约束包含了
#2
C1
处的约束,但反之亦然

然而,我们可以从[temp.constr.order]和[temp.constr.atomic]的兔子洞中看出,即使在较旧的
实现中,也与
相同:

// old impl.; was named Same back then
template<typename T, typename U>
concept same_as = is_same_v<T, U>;
根据上面的(切向)部分,我们还可以注意到,
same\u As
A
B
这两个概念单独包含,而
A
B
单独不包含
same\u


OP:第一个问题是为什么需要一个
SameHelper
概念

根据,只能包含概念。因此,对于概念的较旧实现,如果直接使用了
相同的
转换特征(不是概念),则特征本身不属于包含规则。这意味着实现如下:

template< class T, class U >
concept same_as = std::is_same_v<T, U> && std::is_same_v<U, T>
模板
概念相同=std::is_same_v&&std::is_same_v

将真正包含一个冗余的r.h.s.,用于
&&
,因为类型特征不能包含类型特征。当LWG 3182被解决时,其意图是按照上述语义显示包含关系,添加了一个中间概念来强调包含。

仅仅因为
SameHelper
可能是真的并不正确平均值
SameHelper
可能是。这就是问题所在,如果a等于b,b等于a,不是吗?@user7769147是的,这就是定义这种关系。嗯,文档甚至说“满足了可交换性,即对于任何两种类型的t和U,
都是相同的::value==true
当且仅当
是相同的::value==true
”这意味着这种双重检查是不必要的不,这是错误的,std::is_same说:如果且仅当条件成立,两种类型是可交换的。这不一定如此。但我找不到两种非可交换类型的示例。我同意你的观点,但最后一个论点有点错误
// old impl.; was named Same back then
template<typename T, typename U>
concept same_as = is_same_v<T, U>;
// A and B are concepts
concept same_as = A ^ B
template< class T, class U >
concept same_as = std::is_same_v<T, U> && std::is_same_v<U, T>