I want to check that an attribute of a struct/class fits to my needs with concepts, but compiler complains about.
Example:
struct N
{
char value;
auto Get() { return value; }
};
struct M
{
int value;
auto Get() { return value; }
};
void func3( auto n )
requires requires
{
//{ n.Get() } -> std::same_as<int>;
{ n.value } -> std::same_as<int>;
}
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void func3( auto n )
requires requires
{
//{ n.Get() } -> std::same_as<char>;
{ n.value } -> std::same_as<char>;
}
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
M m;
N n;
func3( n );
func3( m );
}
Results in a bit longer bunch of messages with ( gcc 10.1.1 )
main.cpp: In function 'int main()':
main.cpp:202:18: error: no matching function for call to 'func3(N&)'
202 | func3( n );
| ^
main.cpp:154:10: note: candidate: 'void func3(auto:15) requires requires{{func3::n.value} -> decltype(auto) [requires std::same_as<<placeholder>, int>];} [with auto:15 = N]'
154 | void func3( auto n )
| ^~~~~
main.cpp:154:10: note: constraints not satisfied
main.cpp: In instantiation of 'void func3(auto:15) requires requires{{func3::n.value} -> decltype(auto) [requires std::same_as<<placeholder>, int>];} [with auto:15 = N]':
main.cpp:202:18: required from here
main.cpp:154:10: required by the constraints of 'template<class auto:15> void func3(auto:15) requires requires{{func3::n.value} -> decltype(auto) [requires std::same_as<<placeholder>, int>];}'
main.cpp:155:18: in requirements [with auto:15 = N]
main.cpp:158:13: note: 'n.value' does not satisfy return-type-requirement
158 | { n.value } -> std::same_as<int>;
| ~~^~~~~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
main.cpp:165:10: note: candidate: 'void func3(auto:16) requires requires{{func3::n.value} -> decltype(auto) [requires std::same_as<<placeholder>, char>];} [with auto:16 = N]'
165 | void func3( auto n )
| ^~~~~
main.cpp:165:10: note: constraints not satisfied
main.cpp: In instantiation of 'void func3(auto:16) requires requires{{func3::n.value} -> decltype(auto) [requires std::same_as<<placeholder>, char>];} [with auto:16 = N]':
main.cpp:202:18: required from here
main.cpp:165:10: required by the constraints of 'template<class auto:16> void func3(auto:16) requires requires{{func3::n.value} -> decltype(auto) [requires std::same_as<<placeholder>, char>];}'
main.cpp:166:18: in requirements [with auto:16 = N]
main.cpp:169:13: note: 'n.value' does not satisfy return-type-requirement
169 | { n.value } -> std::same_as<char>;
| ~~^~~~~
main.cpp:203:18: error: no matching function for call to 'func3(M&)'
203 | func3( m );
| ^
main.cpp:154:10: note: candidate: 'void func3(auto:15) requires requires{{func3::n.value} -> decltype(auto) [requires std::same_as<<placeholder>, int>];} [with auto:15 = M]'
154 | void func3( auto n )
| ^~~~~
main.cpp:154:10: note: constraints not satisfied
main.cpp: In instantiation of 'void func3(auto:15) requires requires{{func3::n.value} -> decltype(auto) [requires std::same_as<<placeholder>, int>];} [with auto:15 = M]':
main.cpp:203:18: required from here
The version with checking the return type of the Get()
function works as expected. What is wrong here?
See on compiler explorer
Update ( 12. Nov. 21 )
It seems someone has fixed the bug: bug report
GCC is actually right (when rejecting the code, not going bonkers). To quote the standard
[expr.prim.req.compound]/1.3
- If the return-type-requirement is present, then:
- Substitution of template arguments (if any) into the return-type-requirement is performed.
- The immediately-declared constraint ([temp.param]) of the type-constraint for
decltype((E))
shall be satisfied.
E
is our expression, namely n.value
.
Now, decltype(n.value)
is char
or int
, that's because decltype
has a special rule for class member access and id expressions. But decltype((n.value))
is char&
or int&
. The value category is encoded in the type of decltype
when dealing with a general expression (such as a parenthesized class member access).
Your example works in GCC when we amend it
void func3( auto n )
requires requires
{
//{ n.Get() } -> std::same_as<int>;
{ n.value } -> std::same_as<int&>;
}
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void func3( auto n )
requires requires
{
//{ n.Get() } -> std::same_as<char>;
{ n.value } -> std::same_as<char&>;
}
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}