The following code is a simulation of an error I have in a larger code base. The idea is to collect various methods of calculating hashes of objects under one umbrella. The c++20 requires expression together with c++17 constexpr if provide a way to dispatch different strategies based on the input type.
It is entirely valid c++20 code to uses requires expression without concepts and outside a constraint on a template. A requires expression should just evaluate to a constexpr bool and is thus ok to use inside constexpr if.
The various operations are mocked out so as not to add unnecessary dependencies when demonstrating the issue.
https://godbolt.org/z/o79rTKfY8
#include <iostream>
struct A {
int a ;
std::string GetHashCode() const {
return "BHASH";
}
};
struct B {
int b ;
template <typename Archive>
void serialize(Archive & ar, int ) const
{
// No op
}
};
struct Archive {
std::string GetKey(){
return "GetArchiveKey";
}
};
template <typename T>
void operator & (Archive & ar, T & t){
t.serialize(ar, 0);
}
template <typename T>
std::string foo(T const & t)
{
if constexpr ( requires(Archive ar) { ar & t; }){
Archive ar;
ar & t;
return ar.GetKey();
}else if constexpr( requires { t.GetHashCode(); }){
return t.GetHashCode();
}else
return "NO HASH";
}
int main() {
std::cout << foo(A{}) << std::endl ;
std::cout << foo(B{}) << std::endl ;
}
The error is
<source>:27:7: error: no member named 'serialize' in 'A'
t.serialize(ar, 0);
~ ^
<source>:35:12: note: in instantiation of function template specialization 'operator&<const A>' requested here
ar & t;
^
<source>:45:18: note: in instantiation of function template specialization 'foo<A>' requested here
std::cout << foo(A{}) << std::endl ;
^
1 error generated.
This is strange because the error occurs in the following context.
if constexpr ( requires(Archive ar) { ar & t; }){
Archive ar;
ar & t;
return ar.GetKey();
}
where requires(Archive ar) {ar & t;}
is passing but when the body of the if clause is compiled ar & t
fails to compile. This is because deeper down the type requires a serialize
method. I would have thought this would have been caught in the requires clause, removing this block from consideration.