C++ 概念成员使用引用检查类型歧义
C++ concept member check type ambiquity with reference
我正在学习 C++ 概念,但遇到一个令人讨厌的问题:
我不知道如何区分 int
类型的成员变量和 int&
类型的成员变量。
原因是我正在使用的检查是使用 instance.member 语法,而在 C++ 中 returns 是一个引用。
完整示例:
#include <iostream>
#include <concepts>
template<typename T>
void print(T t) {
std::cout << "generic" << std::endl;
}
template<typename T>
requires requires(T t){
{t.val} -> std::same_as<int&>;
}
void print(T t) {
std::cout << "special" << std::endl;
}
struct S1{
int bla;
};
struct S2{
int val = 47;
};
int x = 47;
struct S3{
int& val=x;
};
int main()
{
print(4.7);
print(S1{});
print(S2{});
print(S3{});
}
我希望 print(S3{})
将由通用案例处理,而不是特殊案例。
请注意,将我的 requires
内容更改为:
{t.val} -> std::same_as<int>;
使 S2
与模板不匹配,因此不起作用(就像我说的,我认为 C++ 中的成员访问 returns 是一个参考)。
有办法解决这个问题吗?
解决方法是:
template <typename T>
requires requires(T t)
{
requires std::is_same_v<decltype(t.val), int>; // or `int &` for references
}
void print(T t)
Clang 有一个错误导致 {T::val} -> std::same_as<int>
也能正常工作,即使 lhs 的类型 said to be 由 decltype((...))
确定,应该 return int &
这里
请注意,“C++ 中的成员访问 return 是一个引用” 是错误的。不可能是这样,因为 expressions can't have reference types。当您使用引用 return 类型编写函数时,调用它会产生非引用类型的左值。
decltype
将根据值类别向表达式类型添加引用性(&
用于左值,&&
用于 xvalues,无纯右值)。这就是为什么人们经常认为表达式可以有引用类型。
对于变量(相对于一般表达式)它也有一个特殊的规则,这导致它return写的变量类型,忽略表达式 类型和值类别。显然 t.val
算作此目的的变量。
这里的问题是表达式概念检查在检查中使用 decltype((e))
而不是 decltype(e)
(额外的括号很重要)。
因为 t.val
是 int
类型的左值(表达式从来没有引用类型),所以 decltype((t.val))
无论如何都是 int&
,正如您所发现的。
相反,您需要明确使用单括号语法:
template <typename T>
requires requires (T t) {
requires std::same_as<decltype(t.val), int&>;
}
void print(T t) {
std::cout << "special" << std::endl;
}
或
template <typename T>
requires std::same_as<decltype(T::val), int&>
我正在学习 C++ 概念,但遇到一个令人讨厌的问题:
我不知道如何区分 int
类型的成员变量和 int&
类型的成员变量。
原因是我正在使用的检查是使用 instance.member 语法,而在 C++ 中 returns 是一个引用。
完整示例:
#include <iostream>
#include <concepts>
template<typename T>
void print(T t) {
std::cout << "generic" << std::endl;
}
template<typename T>
requires requires(T t){
{t.val} -> std::same_as<int&>;
}
void print(T t) {
std::cout << "special" << std::endl;
}
struct S1{
int bla;
};
struct S2{
int val = 47;
};
int x = 47;
struct S3{
int& val=x;
};
int main()
{
print(4.7);
print(S1{});
print(S2{});
print(S3{});
}
我希望 print(S3{})
将由通用案例处理,而不是特殊案例。
请注意,将我的 requires
内容更改为:
{t.val} -> std::same_as<int>;
使 S2
与模板不匹配,因此不起作用(就像我说的,我认为 C++ 中的成员访问 returns 是一个参考)。
有办法解决这个问题吗?
解决方法是:
template <typename T>
requires requires(T t)
{
requires std::is_same_v<decltype(t.val), int>; // or `int &` for references
}
void print(T t)
Clang 有一个错误导致 {T::val} -> std::same_as<int>
也能正常工作,即使 lhs 的类型 said to be 由 decltype((...))
确定,应该 return int &
这里
请注意,“C++ 中的成员访问 return 是一个引用” 是错误的。不可能是这样,因为 expressions can't have reference types。当您使用引用 return 类型编写函数时,调用它会产生非引用类型的左值。
decltype
将根据值类别向表达式类型添加引用性(&
用于左值,&&
用于 xvalues,无纯右值)。这就是为什么人们经常认为表达式可以有引用类型。
对于变量(相对于一般表达式)它也有一个特殊的规则,这导致它return写的变量类型,忽略表达式 类型和值类别。显然 t.val
算作此目的的变量。
这里的问题是表达式概念检查在检查中使用 decltype((e))
而不是 decltype(e)
(额外的括号很重要)。
因为 t.val
是 int
类型的左值(表达式从来没有引用类型),所以 decltype((t.val))
无论如何都是 int&
,正如您所发现的。
相反,您需要明确使用单括号语法:
template <typename T>
requires requires (T t) {
requires std::same_as<decltype(t.val), int&>;
}
void print(T t) {
std::cout << "special" << std::endl;
}
或
template <typename T>
requires std::same_as<decltype(T::val), int&>