Search code examples
c++language-lawyerc++20c++-conceptsdecltype

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?


Solution

  • 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 ->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.