为 std 元组专门化模板而不需要用户做 remove_cvref
Specializing a template for std tuple without requiring users to do remove_cvref
你可能知道,因为 C++ 不支持类型列表,除非在特殊情况下,大多数人只使用元组作为丑陋的类型列表。
因此,例如要检查某个列表是否仅包含 fp 数字,可以这样做:
template <typename T>
struct AllFp {};
template <typename... T>
struct AllFp<std::tuple<T...>> {
static constexpr bool value =
(std::is_floating_point_v<std::remove_cvref_t<T>> && ...);
};
虽然这看起来可行,但如果用户传递 const std::tuple 或对元组的引用,它实际上不起作用。
#include<type_traits>
#include<tuple>
template <typename T>
struct AllFp {};
template <typename... T>
struct AllFp<std::tuple<T...>> {
static constexpr bool value =
(std::is_floating_point_v<std::remove_cvref_t<T>> && ...);
};
int main(){
static_assert(!AllFp<std::tuple<float, int, float>>::value);
static_assert(AllFp<std::tuple<float, long double, float>>::value);
// requires std::remove_cvref_t
//static_assert(AllFp<const std::tuple<float, long double, float>>::value);
//static_assert(AllFp<std::tuple<float, long double, float>&>::value);
}
有没有一种方法可以编写模板,以便“用户”(使用 AllFp 的人)不需要清理他们传递的元组类型?
注意:标记 C++20,因为我对概念解决方案没意见。
对于 Boost.Mp11,这是一个简短的一行(一如既往):
template <typename L>
using AllFp = mp_all_of<std::remove_cvref_t<L>, std::is_floating_point>;
请注意 std::remove_cvref_t
实际上是 C++20。对于较早的标准,为了简洁起见,您可以只做 std::remove_cv_t<std::remove_reference_t<L>>
或只做 std::decay_t<L>
。
更正谓词:
template <typename T>
using decays_to_fp = std::is_floating_point<std::remove_cvref_t<T>>;
template <typename L>
using AllFp = mp_all_of<std::remove_cvref_t<L>, decays_to_fp>;
或者:
template <typename L>
using AllFp = mp_all_of_q<std::remove_cvref_t<L>,
mp_compose<std::remove_cvref_t, std::is_floating_point>>;
您所需要的只是一个额外的间接级别。将您的主要模板和专业化从 AllFp
重命名为 AllFp_Impl
。然后制作 AllFp
别名模板,完成从 tuple
本身中删除 cvref 限定的工作:
template <typename T>
using AllFp = AllFp_Impl<std::remove_cvref_t<T>>;
这里是 demo。
你也可以使用一种自我继承。
我的意思是:您可以添加以下专业
template <typename T>
struct AllFp<T const> : public AllFp<T>
{ };
template <typename T>
struct AllFp<T&> : public AllFp<T>
{ };
以下是仅使用 std::remove_cv_t
的完整编译示例(static_assert()
没有错误)
#include<type_traits>
#include<tuple>
template <typename T>
struct AllFp {};
template <typename T>
struct AllFp<T const> : public AllFp<T>
{ };
template <typename T>
struct AllFp<T&> : public AllFp<T>
{ };
template <typename... T>
struct AllFp<std::tuple<T...>> {
static constexpr bool value =
(std::is_floating_point_v<std::remove_cv_t<T>> && ...);
};
int main(){
static_assert(!AllFp<std::tuple<float, int, float>>::value);
static_assert(AllFp<std::tuple<float, long double, float>>::value);
static_assert(AllFp<const std::tuple<float, long double, float>>::value);
static_assert(AllFp<std::tuple<float, long double, float>&>::value);
}
你可能知道,因为 C++ 不支持类型列表,除非在特殊情况下,大多数人只使用元组作为丑陋的类型列表。 因此,例如要检查某个列表是否仅包含 fp 数字,可以这样做:
template <typename T>
struct AllFp {};
template <typename... T>
struct AllFp<std::tuple<T...>> {
static constexpr bool value =
(std::is_floating_point_v<std::remove_cvref_t<T>> && ...);
};
虽然这看起来可行,但如果用户传递 const std::tuple 或对元组的引用,它实际上不起作用。
#include<type_traits>
#include<tuple>
template <typename T>
struct AllFp {};
template <typename... T>
struct AllFp<std::tuple<T...>> {
static constexpr bool value =
(std::is_floating_point_v<std::remove_cvref_t<T>> && ...);
};
int main(){
static_assert(!AllFp<std::tuple<float, int, float>>::value);
static_assert(AllFp<std::tuple<float, long double, float>>::value);
// requires std::remove_cvref_t
//static_assert(AllFp<const std::tuple<float, long double, float>>::value);
//static_assert(AllFp<std::tuple<float, long double, float>&>::value);
}
有没有一种方法可以编写模板,以便“用户”(使用 AllFp 的人)不需要清理他们传递的元组类型?
注意:标记 C++20,因为我对概念解决方案没意见。
对于 Boost.Mp11,这是一个简短的一行(一如既往):
template <typename L>
using AllFp = mp_all_of<std::remove_cvref_t<L>, std::is_floating_point>;
请注意 std::remove_cvref_t
实际上是 C++20。对于较早的标准,为了简洁起见,您可以只做 std::remove_cv_t<std::remove_reference_t<L>>
或只做 std::decay_t<L>
。
更正谓词:
template <typename T>
using decays_to_fp = std::is_floating_point<std::remove_cvref_t<T>>;
template <typename L>
using AllFp = mp_all_of<std::remove_cvref_t<L>, decays_to_fp>;
或者:
template <typename L>
using AllFp = mp_all_of_q<std::remove_cvref_t<L>,
mp_compose<std::remove_cvref_t, std::is_floating_point>>;
您所需要的只是一个额外的间接级别。将您的主要模板和专业化从 AllFp
重命名为 AllFp_Impl
。然后制作 AllFp
别名模板,完成从 tuple
本身中删除 cvref 限定的工作:
template <typename T>
using AllFp = AllFp_Impl<std::remove_cvref_t<T>>;
这里是 demo。
你也可以使用一种自我继承。
我的意思是:您可以添加以下专业
template <typename T>
struct AllFp<T const> : public AllFp<T>
{ };
template <typename T>
struct AllFp<T&> : public AllFp<T>
{ };
以下是仅使用 std::remove_cv_t
static_assert()
没有错误)
#include<type_traits>
#include<tuple>
template <typename T>
struct AllFp {};
template <typename T>
struct AllFp<T const> : public AllFp<T>
{ };
template <typename T>
struct AllFp<T&> : public AllFp<T>
{ };
template <typename... T>
struct AllFp<std::tuple<T...>> {
static constexpr bool value =
(std::is_floating_point_v<std::remove_cv_t<T>> && ...);
};
int main(){
static_assert(!AllFp<std::tuple<float, int, float>>::value);
static_assert(AllFp<std::tuple<float, long double, float>>::value);
static_assert(AllFp<const std::tuple<float, long double, float>>::value);
static_assert(AllFp<std::tuple<float, long double, float>&>::value);
}