Home > Software design >  Clang accepts program involving requires-constraint while gcc and msvc rejects it
Clang accepts program involving requires-constraint while gcc and msvc rejects it

Time:11-13

Both of the following programs are accepted by clang but rejected by gcc and msvc. Demo

#include <iostream>
#include <type_traits>                                                                                                                                                                                                                                            
#include <functional>                                                                                                                                                                                                                                       

template <typename T>                                                                                                                                                                                                                                           
class myclass {                                                                                                                                                                                                                                                    
public:                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
 
  void func(const T&) requires true    //#1
  {
     std::cout << "true version";
  }
 
  void func(const T&) requires false    //#2
  {
     std::cout << "false version";
  }
};                                                                                                                                                                                                                                                              


int main(){                                                                                                                                                                                                                                
                                                                                                                                                                                                                                             
  myclass<int> obj;                                                                                                                                                                                                                             
  auto mylambda = std::bind(&myclass<int>::func, &obj, 5); //accepted by clang rejected by gcc
  mylambda();                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                    
}  

As we can see the program given above is rejected by gcc with the error:

error: no matching function for call to 'bind(<unresolved overloaded function type>, myclass<int>*, int)'
   24 |   auto mylambda = std::bind(&myclass<int>::func, &obj, 5); //accepted by clang rejected by gcc

If we modify the program a little bit to use the template parameter the program is still rejected by gcc and msvc but accepted by clang. Demo

#include <iostream>
#include <type_traits>                                                                                                                                                                                                                                            
#include <functional>                                                                                                                                                                                                                                       

template<bool B>
struct S {
    void f() requires   B  { std::cout << "true\n"; }     //#1
    void f() requires (!B) { std::cout << "false\n"; }    //#2
};

int main() { 

    S<true> obj{};
    auto mylambda = std::bind(&S<true>::f, &obj) ; //gcc rejects this but clang accepts this
    mylambda();
} 

So my question is which compiler is right here(if any)?

CodePudding user response:

The program is well-formed and clang is correct in accepting the code because the moment we write S<true>, it gets known/fixed which of the two member functions will be available. In other words, for any given S<boolvalue>, it is guaranteed that only one of the member functions is present inside that class S<boolvalue>. Basically, for the second program, version #2 is discarded and shouldn't be considered by name lookup and so the overload set only contains one viable candidate.

Thus, when writing/passing &S<true>::f as an argument to std::bind, it is known that version #1 is to be selected because that is the only choice as the version #2 was discarded and so #2 isn't present as a member function.


The same reasoning applies to the first example. In the 1st example, &myclass::func refers to the version #1 because #1 is the only one available since #2 was discarded.


Note

Also note that the nontemplate member function of a class template is a templated entity and so the use of require-clause is valid in both of the above given programs. From dcl.decl#4:

The optional requires-clause in an init-declarator or member-declarator shall be present only if the declarator declares a templated function ([dcl.fct]). When present after a declarator, the requires-clause is called the trailing requires-clause. The trailing requires-clause introduces the constraint-expression that results from interpreting its constraint-logical-or-expression as a constraint-expression.

Note 2

Note that there is a note in the standard in the context of modules which says:

A discarded declaration is neither reachable nor visible to name lookup outside the module unit, nor in template instantiations whose points of instantiation ([temp.point]) are outside the module unit, even when the instantiation context ([module.context]) includes the module unit. — end note ]


Here is the gcc bug report:

GCC rejects valid program involving requires-clause

Here is the msvc bug report:

MSVC rejects valid program involving requires-clause

  • Related