【发布时间】:2019-10-01 17:43:57
【问题描述】:
我的目标是有一个结构,它接受一个专门的enable_if_t<> 的别名以及一个类型名可变参数包,然后告诉我包中的所有类型是否满足enable_if 的条件.我有一堆专门的enable_ifs,但需要先为它们编写测试,然后才能将它们放入我们的开源项目中。我有大约 2000 多行代码手动测试这些专业化,但我敢打赌,如果我能弄清楚下面的模式,我可以把它提高到 100 或 200 行。我有一个工作版本(+ Godbolt 链接),但是我不确定它为什么工作,并且该方案在实现接收参数包的情况下会中断
这是我想编写的代码示例及其结果。我正在使用 C++14,并且可以从 C++17 中窃取基本实现,例如连词和 void_t
#include <type_traits>
#include <string>
// enable_if for arithmetic types
template <typename T>
using require_arithmetic = typename std::enable_if_t<std::is_arithmetic<T>::value>;
const bool true_arithmetic = require_tester<require_arithmetic, double, int, float>::value;
// output: true
// If any of the types fail the enable_if the result is false
const bool false_arithmetic = require_tester<require_arithmetic, double, std::string, float>::value;
// output: false
以下内容确实可以满足我的要求,但是我不太了解如何操作。
// Base impl
template <template <class> class Check, typename T1, typename = void>
struct require_tester_impl : std::false_type {};
// I'm not totally sure why void_t needs to be here?
template <template <class> class Check, typename T1>
struct require_tester_impl<Check, T1, void_t<Check<T1>>> : std::true_type {};
// The recursive version (stolen conjuction from C++17)
template <template <class> class Check, typename T = void, typename... Types>
struct require_tester {
static const bool value = conjunction<require_tester_impl<Check, T>,
require_tester<Check, Types...>>::value;
};
// For the end
template <template <class> class Check>
struct require_tester<Check, void> : std::true_type {} ;
特别是,我不确定为什么在 impl 的部分特化中需要 void_t 来用于 std::true_type。
我想要的是一个require_variadic_tester,它接受一个可变参数模板化别名,比如enable_if<conjunction<check<T...>>::value>,并给出真或假。可悲的是,无论输入什么类型,下面都会返回 false
// impl
template <template <class...> class Check, typename... Types>
struct require_variadic_impl : std::false_type {};
// Adding void_t here causes the compiler to not understand the partial specialiation
template <template <class...> class Check, typename... Types>
struct require_variadic_impl<Check, Check<Types...>> : std::true_type {};
template <template <class...> class Check, typename... Types>
struct require_variadic_tester : require_variadic_impl<Check, Types...> {};
给定输入,我想要以下内容,但似乎无法动摇如何将该连词隐藏到低一级
// Enable if for checking if all types are arithmetic
template <typename... Types>
using require_all_arithmetic = std::enable_if_t<conjunction<std::is_arithmetic<Types>...>::value>;
require_variadic_tester<require_all_arithmetic, double, double, double>::value;
// is true
require_variadic_tester<require_all_arithmetic, double, std::string, double>::value;
// is false
我认为我在第一个元函数中没有理解void_t导致我的误解
以下是神螺栓,非常感谢任何帮助理解这一点!
编辑:
为了提供更多背景信息,说明我为什么要在上面加上 enable_if_t 的连词。我坚持使用 C++14,但我们正在向我们的开源数学库添加一个新功能,如果没有更多的泛型类型(以及对这些泛型类型的要求),我们最终会出现大量代码膨胀。我们目前有这样的东西
template <int R, int C>
inline Eigen::Matrix<double, R, C> add(
const Eigen::Matrix<double, R, C>& m1, const Eigen::Matrix<double, R, C>& m2) {
return m1 + m2;
}
我想要更多通用模板并做类似的事情
template <typename Mat1, typename Mat2,
require_all_eigen<is_arithmetic, Mat1, Mat2>...>
inline auto add(Mat1&& m1, Mat2&& m2) {
return m1 + m2;
}
我已经设置了所有这些 require_*_<container> 别名,但所有这些的测试需要大约 2000 多行,并且在未来这将是一个必须处理的时髦混乱。
我们有一元和可变模板 enable_if 别名,此时上面的一元案例可以满足我的要求,就像一个很好的测试
#include <gtest/gtest.h>
TEST(requires, arithmetic_test) {
EXPECT_FALSE((require_tester<require_arithmetic, std::string>::value));
EXPECT_TRUE((require_tester<require_arithmetic, double, int, float>::value));
}
我遇到的问题是测试可变参数模板 enable_if 别名,我希望能够在其中编写类似的东西
// Enable if for checking if all types are arithmetic
template <typename... Types>
using require_all_arithmetic = std::enable_if_t<conjunction<std::is_arithmetic<Types>...>::value>;
/// For the tests
TEST(requires, arithmetic_all_test) {
EXPECT_FALSE((require_variadic_tester<require_all_arithmetic, std::string,
Eigen::Matrix<float, -1, -1>>::value));
EXPECT_TRUE((require_variadic_tester<require_all_arithmetic,
double, int, float>::value));
}
如果我可以测试所有这些,我认为仅我们库的 requires 部分就可能是一个不错的仅标头迷你库,用于我所说的“14 中的坏假概念”(或简称 bfc14 ;- ))
【问题讨论】:
-
回答你的第一个问题:如果
Check<T1>格式不正确,那么require_tester_impl<Check, T1, void>将是false_type的派生,因为模板替换在专业化期间失败,退回到非专业化实现,而如果格式正确,它将成功地专门化为true_type的派生。 -
谢谢!我想我不明白的是为什么
Check<T1>病态不会导致 SFINAE 关闭并使用非部分专业化
标签: c++ c++14 c++17 variadic-templates sfinae