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: