为 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);
}