【问题标题】:Specializations of std::common_type - misunderstanding, bug, both, or neither?std::common_type 的特化——误解、错误、两者兼有,还是两者都没有?
【发布时间】:2019-10-10 19:52:45
【问题描述】:

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

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

template <class... T> struct common_type;

除非该特征是特化的(如下文注 B 中所述),否则 成员类型应按照以下注释 A 中的规定定义或省略。 如果省略,则不存在成员类型。中的每种类型 参数包 T 应该是完整的、cv void 或未知数组 绑定。

附注 A...

3.注意 A:对于应用于类型参数包 T 的 common_type trait,成员类型应按如下方式定义或不存在:

(3.1) — 如果sizeof...(T) 为零,则不应有成员类型。

(3.2) — 如果sizeof...(T) 为1,则令T0 表示构成包T 的唯一类型。成员typedef-name type 应表示与common_type_t<T0, T0>; 相同的类型(如果有),否则将没有成员输入。

(3.3)——如果sizeof...(T)为二,则构成T的第一类和第二类分别用T1和T2表示,令D1和D2分别表示与decay_t<T1>decay_t<T2>相同的类型.

(3.3.1) — 如果is_same_v<T1, D1>falseis_same_v<T2, D2>false,让C 表示与common_type_t<D1, D2> 相同的类型(如果有)。

(3.3.2) — 否则,让 C 表示相同的类型,如果有的话,如 decay_t<decltype(false ? declval<D1>() : declval<D2>())> [注意:如果有专业化common_type<D1, D2>,这将不适用。 ——尾注]

在任何一种情况下,成员 typedef-name type 都应表示与 C 相同的类型(如果有)。否则,将没有成员类型。

(3.4) — 如果sizeof...(T) 大于两个,则令 T1、T2 和 R 分别表示构成 T 的第一个、第二个和(一组)剩余类型。令 C 表示相同的类型,如果任何,如common_type_t<T1, T2>。如果存在这样的类型 C,则成员 typedef-name type 应表示与common_type_t<C, R...> 相同的类型(如果有)。否则,将没有成员类型。

和注意 B...

4.注 B:尽管有 23.15.2 的规定,但根据 20.5.4.2.1,程序可以将 common_type<T1, T2> 特化为 T1 和 T2 类型,使得 is_same_v<T1, decay_t<T1>>is_same_v<T2, decay_t<T2>> 都为真。 [注意:当模板参数之间只需要显式转换时,需要这种特殊化。 ——尾注]

这样的特化不需要有一个名为 type 的成员,但如果有,该成员应该是一个 typedef-name 用于可访问且明确的 cv-unqualified 非引用类型 C,其中每个类型 T1 和 T2 都是显式可转换。此外,common_type_t<T1, T2> 应表示与common_type_t<T2, T1> 相同的类型(如果有的话)。违反本说明规则无需诊断。

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

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 中添加了任何 cv 限定符或引用。

对我来说,标准似乎表明第二组断言应该成功,但它们在 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 版本都失败了。

【问题讨论】:

  • @NathanOliver 是的,但是 3.3.1 声明如果你传入 const 版本,它应该衰减它(删除 const),type 字段是 common_type&lt;D1,D2&gt;,即专业的。
  • @Yakk-AdamNevraumont 是的。刚通过它回去。对我来说似乎是一个错误。
  • 不是编译器问题,是标准库实现问题。 libstdc++ 曾经有一个显然已经修复的错误。 libc++ 有这个权利已经有一段时间了。 (tl;博士你是对的)。还有,你不必全力以赴用chrono,可以做点什么much simpler
  • 您的最后一个示例使用 macOS 上的 clang/libc++ 和 -std=c++17 或更高版本为我编译。
  • godbolt.org/z/z8HWlr -- 用 clang 在 Godbolt 上构建示例。刚改成libc++

标签: c++ c++17


【解决方案1】:

这似乎是您的 中的一个错误。 的当前版本没有错误,godbolt 证明了这一点。

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

【讨论】:

  • 库是否应该实现规则 3 的第一部分,以便用户提供的专业化只需要专门化衰减类型 - 还是专业化也必须做衰减的东西?我读它是因为我的专业化只需要为腐烂的类型做 - 图书馆将处理其他人并调用专业化。
  • 我认为这应该是图书馆的工作 - 用户只需实现单一专业化。 clang/libc++ 在这方面做了我所期望的,所以我假设这就是答案。
  • @JodyHagins 如果 trait 不是针对 const 类型的特化,则必须遵循注释 A 中的规则。它们涉及衰减和递归地“调用”common_type。该递归调用可以是专门的,如果是这样,则必须得到尊重。
  • std::common_type 的特化必须遵守 std::decay 必须是恒等变换的规则。我读到这意味着表单模板 struct common_type, Foo> 的特殊化是不允许的,因为 std::decay_t != T1 是第一个参数,即 const Foo。因此,我不确定专业化应该如何解释递归衰减机制。也许我错过了一些很酷的技巧。
  • @jody Note B 定义了何时可以对其进行专业化;你的专业违反了注意B。所以不要这样做。然后注 A 成立。如果您在注释 B 下专门化衰减案例,则 3.3.1 声明未专门化的非衰减案例必须使用您的专门化。
猜你喜欢
  • 2011-10-16
  • 1970-01-01
  • 2014-07-20
  • 1970-01-01
  • 2015-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多