为什么参数映射中的替换失败被认为是格式错误的?

Vai*_*Man 25 c++ language-design language-lawyer c++-concepts c++20

在此代码中,

template<class T, class U>
concept always_true = true;

template<class T>
concept always_true_if_tagged = always_true<T, typename T::tag>;

struct A {
    using tag = int;
};

static_assert(always_true_if_tagged<A>);
static_assert(!always_true_if_tagged<int>);  //GCC says this failed
Run Code Online (Sandbox Code Playgroud)

GCC 表示第二个断言失败。Clang 和 MSVC 都同意编译它。

我最初认为它是不正确的,不需要诊断,因为temp.constr.normal#1.4

概念 ID 的范式C<A1, A2, ..., An>是 的约束表达式的范式C,在替换A1, A2, ..., An每个C原子约束中的参数映射中的各自模板参数后。如果任何此类替换导致无效类型或表达式,则该程序格式错误;无需诊断。

替换T::typename tag是 的参数映射always_true,因此格式错误;无需诊断。

所以我的前两个问题是

  1. 我说得对吗?(格式是否错误?我是否引用了正确的原因?)
  2. 为什么它应该是格式错误的?(如果我是对的。)

解决方案之一是检查之前的嵌套类型名。所以参数映射always_true不会发生。

template<class T>
concept always_true_if_tagged =
    requires {typename T::tag;}
    && always_true<T, typename T::tag>;
Run Code Online (Sandbox Code Playgroud)

此外,temp.constr.atomic#3说

为了确定是否满足原子约束,首先将参数映射和模板实参替换到其表达式中。如果替换导致无效类型或表达式,则不满足约束。否则,如有必要,将执行左值到右值的转换,并且E应为 类型的常量表达式bool。E当且仅当评估结果为 时,约束才被满足true。如果在程序的不同点,对于相同的原子约束和模板参数,满足结果不同,则该程序是格式错误的,不需要诊断。

如果我写

template<class T>
concept always_true_if_tagged = bool(always_true<T, typename T::tag>);
Run Code Online (Sandbox Code Playgroud)

bool(always_true<T, typename T::tag>)是原子约束 IIUC。T::typename tagwith的替换会T=int导致无效类型,因此它应该是格式良好的且不满足。

所以我的最后两个(或四个)问题是

  1. 为什么这不适用于第一个代码或者为什么 [temp.constr.normal#1.4] 不适用于此处?
  • 3.1. 这个替换是concept-id的参数映射吗always_true?

  • 3.2. always_true是在concept C = always_true<T, typename T::tag>原子约束中使用吗?temp.constr.constr#general-1表示存在三种不同类型的约束:合取、析取和原子约束。

  1. 为什么不能concept C = always_true<T, typename T::tag>像T=int这样的形式良好呢?(不过可能与第二个问题相同)

编辑:正如我在评论中所说,我注意到这个已回答的问题。但这是一个“WHY”的问题,不仅仅是问标准中定义了什么,更重要的是动机是什么。例如,标准不能考虑always_true_if_tagged<int>格式良好且不满足的原因是什么,正如我在该答案的评论中所问的那样(并且尚未回复),我们可以通过仅添加requires {typename T;}but typename Tis已经在参数列表中来解决这个问题,这使得解决方案似乎多余。

Vir*_*co_ -4

    \n
  1. 你猜对了吗?[\xe2\x80\xa6]
    \ntypename T::tag是的\xe2\x80\x99t 不会使用[T = int].

    \n
  2. \n
  3. 为什么它应该是格式错误的?
    \n你说的理由是对的。typename int::tag简直是无稽之谈。
    \n根据记忆,您\xe2\x80\x99d 还需要将模板专业化移动到 下方以在模板中struct A使用。typename A::tag

    \n
  4. \n
  5. 为什么 [\xe2\x80\xa6]
    \n我不\xe2\x80\x99t相信这是格式良好的,因为\xc2\xa77.3.2 [conv.lval]
    \n我不\xe2\x80\x99t 有答案问题,只有更多问题。

    \n
  6. \n
\n

在概念中,它\xe2\x80\x99s不是模板实例化(请参阅:[temp.concept]、[temp.spec]、[temp.dep]),所以我不\xe2\x80\x99不明白那是如何实现的是一个原子约束(它是一个常量表达式吗?它如何计算出任何值)。

\n

clang 14.0.0 不会\xe2\x80\x99t 生成带有-fsanitize=undefinedon 的警告

\n

另请参阅:
\n SFINAE

\n

  • *我不相信这是格式良好的,因为§7.3.2 [conv.lval]。*我认为我在OP中引用的[temp.constr.atomic#3]说*参数映射和模板参数是* *first** 代入其表达式。如果替换导致无效类型或表达式,则不满足约束**。**否则**,如有必要,将执行左值到右值的转换 [...]* (3认同)