c++ - std::common_type 的特化 - 误解、错误、两者兼而有之?

标签 c++ c++17

用 C++17 标记,因为这是我引用的规范版本。

IMO,这是该标准的相关部分,第 23.15.7.6 节 [meta.trans.other]。

template <class... T> struct common_type;

Unless this trait is specialized (as specified in Note B, below), the member type shall be defined or omitted as specified in Note A, below. If it is omitted, there shall be no member type. Each type in the parameter pack T shall be complete, cv void, or an array of unknown bound.

附注A...

3. Note A: For the common_type trait applied to a parameter pack T of types, the member type shall be either defined or not present as follows:

(3.1) — If sizeof...(T) is zero, there shall be no member type.

(3.2) — If sizeof...(T) is one, let T0 denote the sole type constituting the pack T. The member typedef-name type shall denote the same type, if any, as common_type_t<T0, T0>; otherwise there shall be no member type.

(3.3) — If sizeof...(T) is two, let the first and second types constituting T be denoted by T1 and T2, respectively, and let D1 and D2 denote the same types as decay_t<T1> and decay_t<T2>, respectively.

(3.3.1) — If is_same_v<T1, D1> is false or is_same_v<T2, D2> is false, let C denote the same type, if any, as common_type_t<D1, D2>.

(3.3.2) — Otherwise, let C denote the same type, if any, as decay_t<decltype(false ? declval<D1>() : declval<D2>())> [ Note: This will not apply if there is a specialization common_type<D1, D2>. — end note ]

In either case, the member typedef-name type shall denote the same type, if any, as C. Otherwise, there shall be no member type.

(3.4) — If sizeof...(T) is greater than two, let T1, T2, and R, respectively, denote the first, second, and (pack of) remaining types constituting T. Let C denote the same type, if any, as common_type_t<T1, T2>. If there is such a type C, the member typedef-name type shall denote the same type, if any, as common_type_t<C, R...>. Otherwise, there shall be no member type.

和注释 B...

4. Note B: Notwithstanding the provisions of 23.15.2, and pursuant to 20.5.4.2.1, a program may specialize common_type<T1, T2> for types T1 and T2 such that is_same_v<T1, decay_t<T1>> and is_same_v<T2, decay_t<T2>> are each true. [Note: Such specializations are needed when only explicit conversions are desired between the template arguments. — end note ]

Such a specialization need not have a member named type, but if it does, that member shall be a typedef-name for an accessible and unambiguous cv-unqualified non-reference type C to which each of the types T1 and T2 is explicitly convertible. Moreover, common_type_t<T1, T2> shall denote the same type, if any, as does common_type_t<T2, T1>. No diagnostic is required for a violation of this Note’s rules.

基于此,我希望以下断言能够通过(它们确实如此)...

static_assert(not std::is_const_v<std::common_type_t<const volatile int, short>>);
static_assert(not std::is_volatile_v<std::common_type_t<const volatile int, short>>);
static_assert(std::is_same_v<std::common_type_t<int, short>, std::common_type_t<short, const volatile int>>);
static_assert(std::is_same_v<std::common_type_t<int &, short>, std::common_type_t<int, short>>);

同样,我希望这些也能通过......

using D1 = std::chrono::duration<int, std::ratio<2, 20>>;
using D2 = std::chrono::duration<short, std::ratio<7, 100>>;
static_assert(not std::is_const_v<std::common_type_t<const D1, D2>>);
static_assert(not std::is_volatile_v<std::common_type_t<volatile D1, D2>>);
static_assert(std::is_same_v<std::common_type_t<D1, D2>, std::common_type_t<const D1, D2>>);
static_assert(std::is_same_v<std::common_type_t<D1 &, D2>, std::common_type_t<D1, D2>>);

但是,我在调用 std::common_type 时遇到编译器错误将任何简历限定符或引用添加到 std::chrono::duration .

对我来说,该标准似乎表明第二组断言应该成功,但它们在 clang 和 gcc 中都失败了。因此,我认为我误读了标准 - 或者两个供应商都有相同的错误,这有点不太可能(而且在押注谁错了时我总是选择我)。

也许“除非这个特征是专门化的(如下面的注释 B 中所指定)”这句话意味着注释 A 第 3.3 节的租户都没有申请专门化,但这意味着我们可以合法地得到相当奇怪的结果...就像询问时出现编译错误...

using D1 = std::chrono::duration<int, std::ratio<2, 20>>;
using D2 = std::chrono::duration<short, std::ratio<7, 100>>;
static_assert(not std::is_const_v<std::common_type_t<const D1, D2>>);

正常使用std::common_type不需要在调用之前删除 cv 限定符和引用 - 但似乎特化可能要求调用者必须删除 cv 限定符和引用。

这似乎是错误的......在某个地方。所以,我想弄清楚……规范、供应商实现还是我的想法是否有问题?


更新

看来编译器资源管理器中的 gcc/trunk 成功编译了我认为应该编译的所有内容,尽管 gcc 9.2 失败了,以及所有 clang 版本。

最佳答案

这似乎是您的 中的错误。 的当前版本没有错误,如 godbolt 所示.

3.3.1 确实要求它衰减参数并递归; 不这样做。

关于c++ - std::common_type 的特化 - 误解、错误、两者兼而有之?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58330207/

相关文章:

c++17 - vector<reference_wrapper> ..事情超出了范围?它是如何工作的?

c++ - 这是重载提供与非静态成员函数相同接口(interface)的静态成员函数的优雅方法吗?

c++ - 为什么 std::optional<int> 的构造比 std::pair<int, bool> 更昂贵?

c++ - 用于实现一副纸牌的链表与动态数组? C++

c++ - C++ 中的编译时数组选择

c++ - 推导函数参数包的模板参数是否有缺陷

c++ - 替代可在 64 位 Linux 上运行的 googleperf 工具

c++ - 给定数组时 cout 如何工作?

C++ 编译器错误 "was not declared in this scope"

c++ - 理解这个 Scala 代码