用概念检查属性的类型
checking type of attribute with concepts
我想检查 struct/class 的属性是否符合我对概念的需求,但编译器抱怨。
示例:
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 );
}
使用 ( 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
检查 Get()
函数的 return 类型的版本按预期工作。这里有什么问题?
- clang:按预期工作
- gcc 10.1.1 失败并显示错误消息
- gcc 主干:ICE!瑞银:-
更新(11 月 21 日 12 日)
- gcc 主干(版本 12.x.x。)有效
似乎有人修复了这个错误:
bug report
GCC 实际上是正确的(当拒绝代码时,不要发疯)。引用标准
[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
是我们的表达式,即n.value
.
现在,decltype(n.value)
是 char
或 int
,那是因为 decltype
has a special rule for class member access and id expressions。但是 decltype((n.value))
是 char&
或 int&
。值类别在处理一般表达式(如带括号的class成员访问)时编码为decltype
类型。
当我们修改您的示例时,您的示例在 GCC 中有效
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;
}
根据[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. [Example: Given
concepts C
and D
,
requires {
{ E1 } -> C;
{ E2 } -> D<A1, ⋯, An>;
};
is equivalent to
requires {
E1; requires C<decltype((E1))>;
E2; requires D<decltype((E2)), A1, ⋯, An>;
};
(including in the case where n
is zero). — end example
]
并且,从 [dcl.type.decltype]/1, particularly [dcl.type.decltype]/1.5 适用
For an expression E
, the type denoted by decltype(E)
is defined as
follows:
- [...]
- (1.5) otherwise, if
E
is an lvalue, decltype(E)
is T&
, where T
is the type of E
;
因此,由于 value
是 { n.value } -> std::same_as<int>;
表达式中的 lvalue
,return-type-requirement -> std::same_as<...>;
for the { n.value }
expression need to be matched for return type int&
:
#include <iostream>
#include <type_traits>
template <typename T> struct Foo {
T value;
auto getValue() { return value; }
};
using FooInt = Foo<int>;
using FooChar = Foo<char>;
void abbreviatedFunctionTemplate(auto n) requires requires {
{ n.value } -> std::same_as<int&>; }
{ std::cout << "int overload\n" << std::endl; }
void abbreviatedFunctionTemplate(auto n) requires requires {
{ n.getValue() } ->std::same_as<char>; }
{ std::cout << "char overload\n" << std::endl; }
int main() {
abbreviatedFunctionTemplate(FooInt{}); // int overload
abbreviatedFunctionTemplate(FooChar{}); // char overload
}
我想检查 struct/class 的属性是否符合我对概念的需求,但编译器抱怨。
示例:
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 );
}
使用 ( 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
检查 Get()
函数的 return 类型的版本按预期工作。这里有什么问题?
- clang:按预期工作
- gcc 10.1.1 失败并显示错误消息
- gcc 主干:ICE!瑞银:-
更新(11 月 21 日 12 日)
- gcc 主干(版本 12.x.x。)有效
似乎有人修复了这个错误: bug report
GCC 实际上是正确的(当拒绝代码时,不要发疯)。引用标准
[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
是我们的表达式,即n.value
.
现在,decltype(n.value)
是 char
或 int
,那是因为 decltype
has a special rule for class member access and id expressions。但是 decltype((n.value))
是 char&
或 int&
。值类别在处理一般表达式(如带括号的class成员访问)时编码为decltype
类型。
当我们修改您的示例时,您的示例在 GCC 中有效
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;
}
根据[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. [Example: Given conceptsC
andD
,requires { { E1 } -> C; { E2 } -> D<A1, ⋯, An>; };
is equivalent to
requires { E1; requires C<decltype((E1))>; E2; requires D<decltype((E2)), A1, ⋯, An>; };
(including in the case where
n
is zero). — end example ]
并且,从 [dcl.type.decltype]/1, particularly [dcl.type.decltype]/1.5 适用
For an expression
E
, the type denoted bydecltype(E)
is defined as follows:
- [...]
- (1.5) otherwise, if
E
is an lvalue,decltype(E)
isT&
, whereT
is the type ofE
;
因此,由于 value
是 { n.value } -> std::same_as<int>;
表达式中的 lvalue
,return-type-requirement -> std::same_as<...>;
for the { n.value }
expression need to be matched for return type int&
:
#include <iostream>
#include <type_traits>
template <typename T> struct Foo {
T value;
auto getValue() { return value; }
};
using FooInt = Foo<int>;
using FooChar = Foo<char>;
void abbreviatedFunctionTemplate(auto n) requires requires {
{ n.value } -> std::same_as<int&>; }
{ std::cout << "int overload\n" << std::endl; }
void abbreviatedFunctionTemplate(auto n) requires requires {
{ n.getValue() } ->std::same_as<char>; }
{ std::cout << "char overload\n" << std::endl; }
int main() {
abbreviatedFunctionTemplate(FooInt{}); // int overload
abbreviatedFunctionTemplate(FooChar{}); // char overload
}