如何使用类型特征和概念检测指向算术类型的指针?
How to detect a pointer to an arithmetic type using type traits and concepts?
如何编写检测算术类型指针的概念?
template <typename T>
concept arithmetic = std::is_arithmetic<T>::value;
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
{ *a } -> arithmetic;
};
template <typename T>
void fn() {
printf("fail\n");
}
template <pointer_to_arithmetic T>
void fn() {
printf("pass\n");
}
struct s{};
int main() {
fn<int>();
fn<int*>();
fn<s>();
fn<s*>();
}
我尝试了上面的方法,它编译了但没有按预期运行。
预期输出为:
fail
pass
fail
fail
相反,我得到:
fail
fail
fail
fail
如果我将*a
替换为a[0]
也不起作用。
这应该可以解决问题:
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
requires std::is_arithmetic_v<std::remove_cvref_t<decltype(*a)>>;
requires std::is_pointer_v<T>;
};
...
pointer_to_arithmetic<int> -> false
pointer_to_arithmetic<float> -> false
pointer_to_arithmetic<std::string*> -> false
pointer_to_arithmetic<int*> -> true
pointer_to_arithmetic<float*> -> true
第一行检测可以取消引用为算术类型的任何类型。这可能不是你需要的。所以你需要添加第二行来检测 T
是否确实是指针类型。
对于复合需求中的表达式 E
,类型约束谓词被馈送 decltype((E))
1.
decltype
将表达式的值类别编码为它推导的类型。因为 *p
是一个左值表达式。对于某些 T
.
,推导的类型是 T&
因此您可能希望将您的一对概念重写为
template <typename T>
concept arithmetic_ref = std::is_arithmetic<std::remove_reference_t<T>>::value;
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
{ *a } -> arithmetic_ref ;
};
原子谓词可能可以更好地命名。
当然,这留下了几个悬而未决的问题。你只是鸭子打字,所以任何类似指针的类型(甚至 std::optional
有 operator*
)都是允许的吗?还是您只追求基本的指针类型?该概念应如何处理 cv 限定类型(目前不允许使用它们)?
根据你如何回答这些问题,这个概念可以进一步调整。
您的 arithmetic
特征的类型是 int&
和 s&
,而不是 int
和 s
。解决此问题的一种方法是从 *a
表达式中删除引用:
template<typename T>
auto decay(T&& t) -> std::remove_cvref_t<T> {
return t;
}
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
{ decay(*a) } -> arithmetic;
};
解决此问题的另一种方法是让 arithmetic
将对算术类型的引用识别为算术,因为您仍然可以 使用 以相同方式使用变量。这在另一个答案中有所介绍,尽管对概念名称进行了更改。
如何编写检测算术类型指针的概念?
template <typename T>
concept arithmetic = std::is_arithmetic<T>::value;
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
{ *a } -> arithmetic;
};
template <typename T>
void fn() {
printf("fail\n");
}
template <pointer_to_arithmetic T>
void fn() {
printf("pass\n");
}
struct s{};
int main() {
fn<int>();
fn<int*>();
fn<s>();
fn<s*>();
}
我尝试了上面的方法,它编译了但没有按预期运行。
预期输出为:
fail
pass
fail
fail
相反,我得到:
fail
fail
fail
fail
如果我将*a
替换为a[0]
也不起作用。
这应该可以解决问题:
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
requires std::is_arithmetic_v<std::remove_cvref_t<decltype(*a)>>;
requires std::is_pointer_v<T>;
};
...
pointer_to_arithmetic<int> -> false
pointer_to_arithmetic<float> -> false
pointer_to_arithmetic<std::string*> -> false
pointer_to_arithmetic<int*> -> true
pointer_to_arithmetic<float*> -> true
第一行检测可以取消引用为算术类型的任何类型。这可能不是你需要的。所以你需要添加第二行来检测 T
是否确实是指针类型。
对于复合需求中的表达式 E
,类型约束谓词被馈送 decltype((E))
1.
decltype
将表达式的值类别编码为它推导的类型。因为 *p
是一个左值表达式。对于某些 T
.
T&
因此您可能希望将您的一对概念重写为
template <typename T>
concept arithmetic_ref = std::is_arithmetic<std::remove_reference_t<T>>::value;
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
{ *a } -> arithmetic_ref ;
};
原子谓词可能可以更好地命名。
当然,这留下了几个悬而未决的问题。你只是鸭子打字,所以任何类似指针的类型(甚至 std::optional
有 operator*
)都是允许的吗?还是您只追求基本的指针类型?该概念应如何处理 cv 限定类型(目前不允许使用它们)?
根据你如何回答这些问题,这个概念可以进一步调整。
您的 arithmetic
特征的类型是 int&
和 s&
,而不是 int
和 s
。解决此问题的一种方法是从 *a
表达式中删除引用:
template<typename T>
auto decay(T&& t) -> std::remove_cvref_t<T> {
return t;
}
template <typename T>
concept pointer_to_arithmetic = requires (T a) {
{ decay(*a) } -> arithmetic;
};
解决此问题的另一种方法是让 arithmetic
将对算术类型的引用识别为算术,因为您仍然可以 使用 以相同方式使用变量。这在另一个答案中有所介绍,尽管对概念名称进行了更改。