使用概念允许在成员函数上使用decltype吗?

huangapple go评论63阅读模式
英文:

Does using concepts allow using decltype on member function

问题

I came to know that for a class X with a member function named func, the c++ standard doesn't allow us to write decltype(X::func). Thus I expected the below given program to produce an error saying something like invalid use of non-static member function. But to my surprise, the program compiles with all 3 compilers.

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

template <typename T>
concept test = std::same_as<decltype(T::func), int(int)>;

struct D
{
    int func(int);
};

int main()
{ 
    std::cout << test<D>;      
}    

The above program is compiled by all 3 compilers. By looking at [expr.prim.id] it seems the program should be ill-formed and a diagnostic should be emitted. But none of the compilers provide any error. So, does the use of concept in a way shown in the program make the program well-formed or is it still ill-formed and are all compilers wrong?

英文:

I came to know that for a class X with a member function named func, the c++ standard doesn't allow us to write decltype(X::func). Thus I expected the below given program to produce an error saying something like invalid use of non-static member function. But to my surprise, the program compiles with all 3 compilers.

#include &lt;iostream&gt;
#include &lt;type_traits&gt; 
#include &lt;concepts&gt;

template &lt; typename T &gt;
concept test = std::same_as &lt;decltype(T::func), int(int) &gt;;

struct D
{
    int func(int);
};

int main()
{ 
    std::cout &lt;&lt; test&lt;D&gt;;      
}    

The above program is compiled by all 3 compilers. By looking at [expr.prim.id] it seems the program should be ill-formed and a diagnostic should be emitted. But none of the compilers provide any error. So, does the use of concept in a way shown in the program make the program well-formed or is it still ill-formed and are all compilers wrong?

答案1

得分: 6

程序被解释如下。根据 [temp.constr.normal],表达式 E 的正常形式是一个约束 (13.5.2),其定义如下:

  • 概念标识符 C<A1, A2, ..., An> 的正常形式是 C 的约束表达式的正常形式,在每个原子约束中用 A1、A2、...、An 替换 C 的相应模板参数的参数映射。如果这样的替换导致无效的类型或表达式,则程序是非良构的;不需要诊断。

请注意最后一句的强调。特别是关于参数映射。也就是说,如果参数映射导致无效类型,那么程序将是非良构的。

但是在您给出的示例中,参数映射是 T -&gt;decltype(T::func),这本身不是无效类型。因此,上述引用的要求未得到满足。


现在,从 [temp.constr.atomic#3]:

要确定原子约束是否满足,首先将参数映射和模板参数代入其表达式。如果替换导致无效的类型或表达式,则约束不满足。否则,如果必要,执行左值到右值的转换,E 应该是布尔类型的常量表达式。仅当对 E 的评估结果为 true 时,约束才满足。如果在程序的不同点上,对于相同的原子约束和模板参数,满足结果不同,程序是非良构的,不需要诊断。

因为模板参数 D 的替换导致一个无效的构造 decltype(D::func),所以给定约束不满足(意味着它是 false)。


所有编译器都是正确的,接受该程序并输出 0

英文:

> Does the use of concept in a way shown in the program make the program well-formed

The program is well-formed as explained below. From [temp.constr.normal]:

> The normal form of an expression E is a constraint (13.5.2) that is defined as follows:
> * The normal form of a concept-id C<A1, A2, ..., An> is the normal form of the constraint-expression
of C, after substituting A1, A2, ..., An for C’s respective template parameters in the parameter
mappings in each atomic constraint. If any such substitution results in an invalid type or expression,
the program is ill-formed; no diagnostic is required.

(emphasis mine)

Note the emphasis on the last sentence. In particular, it is about parameter mapping. That is, if the parameter mapping results in an invalid type, then only the program will be IFNDR.

But in your given example, the parameter mapping is T -&gt;decltype(T::func) which is not in itself an invalid type. Thus, the above quoted reference is not satisfied.


Now, from [temp.constr.atomic#3]:

> To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression.
If substitution results in an invalid type or expression, the constraint is not satisfied.

Otherwise, the lvalue-to-rvalue conversion is performed if necessary, and E shall be a constant expression of type bool.
The constraint is satisfied if and only if evaluation of E results in true.
If, at different points in the program, the satisfaction result is different for identical atomic constraints and template arguments, the program is ill-formed, no diagnostic required.

(emphasis mine)

And since the substitution of the template argument D results in an invalid construct decltype(D::func), the given constraint is not satisfied(meaning it is false).


> are all compilers wrong?

Thus, all compilers are correct in accepting the program and outputting 0.

答案2

得分: 3

Substitution failure during satisfaction checking of constraints does not result in the program being ill-formed; it simply causes the constraint to not be satisfied. The relevant text from the standard here is [temp.constr.atomic]/3:
> To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression. If substitution results in an invalid type or expression, the constraint is not satisfied. Otherwise...

Per [temp.names]/9, to determine the value of test&lt;D&gt; we first need to reduce test's constraint expression to its normal form. For the sake of convenience, let's assume same_as&lt;T, U&gt; is defined as std::is_same_v&lt;T, U&gt; (in actuality it's slightly different, but that doesn't affect this answer).

Normalizing the constraint expression of test results in an atomic constraint std::is_same_v&lt;T, U&gt; with the mapping [T -> decltype(T::func), U -> int(int)]. Note that the parameter mappings denote perfectly valid types (one dependent), so [temp.constr.normal]/1.4 (the IFNDR case) does not apply.

An invalid expression is only formed when D is substituted for (test's) T, which according to the text quoted above simply means that test&lt;D&gt; is false, and the program is well-formed.

英文:

Substitution failure during satisfaction checking of constraints does not result in the program being ill-formed; it simply causes the constraint to not be satisfied. The relevant text from the standard here is [temp.constr.atomic]/3:
> To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its expression. If substitution results in an invalid type or expression, the constraint is not satisfied. Otherwise...

Per [temp.names]/9, to determine the value of test&lt;D&gt; we first need to reduce test's constraint expression to its normal form. For the sake of convenience, let's assume same_as&lt;T, U&gt; is defined as std::is_same_v&lt;T, U&gt; (in actuality it's slightly different, but that doesn't affect this answer).

Normalizing the constraint expression of test results in an atomic constraint std::is_same_v&lt;T, U&gt; with the mapping [T -> decltype(T::func), U -> int(int)]. Note that the parameter mappings denote perfectly valid types (one dependent), so [temp.constr.normal]/1.4 (the IFNDR case) does not apply.

An invalid expression is only formed when D is substituted for (test's) T, which according to the text quoted above simply means that test&lt;D&gt; is false, and the program is well-formed.

huangapple
  • 本文由 发表于 2023年5月7日 12:09:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76192159.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定