提问者:小点点

为什么same_as概念检查类型相等性两次?


在https://en.cppreference.com/w/cpp/concepts/same_as上查看same_as概念的可能实现时,我注意到发生了一些奇怪的事情。

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概念? 第二个是为什么same_as检查t是否与u相同,u是否与t相同? 这不是多余的吗?


共2个答案

匿名用户

有趣的问题。 我最近看了Andrew Sutton关于概念的演讲,在问答环节有人问了以下问题(以下链接中的时间戳):CppCon 2018:Andrew Sutton“60年的概念:你需要知道的一切和你不知道的一切”

所以问题可以归结为:如果我有一个概念,它表示一个&&&; B和; C,另一个说C&&; B和; A,它们是否等价?Andrew回答是的,但指出编译器有一些内部方法(对用户是透明的)来将概念分解成原子逻辑命题(原子约束Andrew对术语的措辞)并检查它们是否等价。

现在看看cppreference对std::same_as:

std::same_as包含std::same_as,反之亦然。

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

我猜想这里的原子约束是std::is_same_v。 编译器处理std::is_same_v的方式可能会使它们认为std::is_same_vstd::is_same_v是两个不同的约束(它们是不同的实体!)。 因此,如果您只使用其中一个实现std::same_as:

template< class T, class U >
concept same_as = detail::SameHelper<T, U>;

那么std::same_asstd::same_as将“爆炸”到不同的原子约束,变得不等价。

编译器为什么要关心呢?

请考虑以下示例:

#include <type_traits>
#include <iostream>
#include <concepts>

template< class T, class U >
concept SameHelper = std::is_same_v<T, U>;

template< class T, class U >
concept my_same_as = SameHelper<T, U>;

// template< class T, class U >
// concept my_same_as = SameHelper<T, U> && SameHelper<U, T>;

template< class T, class U> requires my_same_as<U, T>
void foo(T a, U b) {
    std::cout << "Not integral" << std::endl;
}

template< class T, class U> requires (my_same_as<T, U> && std::integral<T>)
void foo(T a, U b) {
    std::cout << "Integral" << std::endl;
}

int main() {
    foo(1, 2);
    return 0;
}

理想情况下,my_same_as; (&&) std::integral包含my_same_as; 因此,编译器应该选择第二个模板专门化,除了。。。它没有:编译器发出一个错误错误:重载'foo(int,int)‘的调用是不明确的

这背后的原因是,由于my_same_asmy_same_as不包含彼此,因此my_same_as; (&&) std::integralmy_same_as变得不可比(在包含关系下的偏序约束集上)。

但是,如果替换

template< class T, class U >
concept my_same_as = SameHelper<T, U>;

template< class T, class U >
concept my_same_as = SameHelper<T, U> && SameHelper<U, T>;

代码将编译。

匿名用户

std::is_same当且仅当:

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

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

给出了这个假设,是_same_vgt; (&&) is_same_v确实是多余的。 但是is_same_v中没有指定same_as; 那只是为了说明。

两者的显式检查允许same-as-impl的实现满足same-as而不是可交换的。 以这种方式指定它可以准确地描述概念的行为方式,而不会限制它的实现方式。

我不知道为什么选择这种方法而不是用is_same_v来指定。 所选方法的一个优点是可以证明这两个定义是分离的。 一个不依赖另一个。