考虑以下函数,它在编译时根据参数类型计算积分或浮点模数:
template<typename T>
constexpr T modulo(const T x, const T y)
{
return (std::is_floating_point<T>::value) ? (x < T() ? T(-1) : T(1))*((x < T() ? -x : x)-static_cast<long long int>((x/y < T() ? -x/y : x/y))*(y < T() ? -y : y))
: (static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(x)
%static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(y));
}
可以改善这个功能的主体吗? (我需要为整数和浮点类型都有一个函数)。
答案 0 :(得分:5)
这是清理它的一种方法:
#include <type_traits>
#include <cmath>
template <typename T> // integral? floating point?
bool remainder_impl(T a, T b, std::true_type, std::false_type) constexpr
{
return a % b; // or whatever
}
template <typename T> // integral? floating point?
bool remainder_impl(T a, T b, std::false_type, std::true_type) constexpr
{
return std::fmod(a, b); // or substitute your own expression
}
template <typename T>
bool remainder(T a, T b) constexpr
{
return remainder_impl<T>(a, b,
std::is_integral<T>(), std::is_floating_point<T>());
}
如果尝试在非算术类型上调用此函数,则会出现编译错误。
答案 1 :(得分:2)
我宁愿用这种方式定义它(模板别名+模板重载):
#include <type_traits>
using namespace std;
// For floating point types
template<typename T, typename enable_if<is_floating_point<T>::value>::type* p = nullptr>
constexpr T modulo(const T x, const T y)
{
return (x < T() ? T(-1) : T(1)) * (
(x < T() ? -x : x) -
static_cast<long long int>((x/y < T() ? -x/y : x/y)) * (y < T() ? -y : y)
);
}
// For non-floating point types
template<typename T>
using TypeToCast = typename conditional<is_floating_point<T>::value, int, T>::type;
template<typename T, typename enable_if<!is_floating_point<T>::value>::type* p = nullptr>
constexpr T modulo(const T x, const T y)
{
return (static_cast<TypeToCast<T>>(x) % static_cast<TypeToCast<T>>(y));
}
int main()
{
constexpr int x = modulo(7.0, 3.0);
static_assert((x == 1.0), "Error!");
return 0;
}
IMO比较长但更干净。我假设“单一功能”是指“可以统一调用的东西”。如果您的意思是“单个函数模板”,那么我只会保持模板别名改进并保留过载。但是,正如另一个答案中所提到的,不清楚为什么你需要只有一个功能模板。
答案 2 :(得分:1)
你问,
“这个功能的主体可以改进吗?”
当然可以。现在这是一个意大利面条混乱:
template<typename T>
constexpr T modulo(const T x, const T y)
{
return (std::is_floating_point<T>::value) ? (x < T() ? T(-1) : T(1))*((x < T() ? -x : x)-static_cast<long long int>((x/y < T() ? -x/y : x/y))*(y < T() ? -y : y))
: (static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(x)
%static_cast<typename std::conditional<std::is_floating_point<T>::value, int, T>::type>(y));
}
你澄清了......
“(我需要为整数和浮点类型都有一个函数)”
模板不是单一功能。这是一个模板。函数是从它生成的。
这意味着您的问题建立在错误的假设之上。
如果删除了这个假设,那么简化函数体的一种方法是将浮点类型与其他数值类型相比较,这是你应该做的事情。为此,将函数模板实现放在一个类中(因为C ++不支持函数的部分特化,只支持类的特殊化。)
然后你可以使用各种格式化技巧,包括“0?0:blah”技巧,使函数更具可读性,包括行和缩进和东西! : - )
附录:深入研究您的代码我看到您随意投放到long int
和int
而忽略了调用者的类型。那是不好的。编写一堆自动化测试用例,调用具有各种参数类型和大/小值的函数可能是一个好主意。
答案 3 :(得分:1)
template <class T>
constexpr
T
modulo(T x, T y)
{
typedef typename std::conditional<std::is_floating_point<T>::value,
int,
T
>::type Int;
return std::is_floating_point<T>() ?
x - static_cast<long long>(x / y) * y :
static_cast<Int>(x) % static_cast<Int>(y);
}
答案 4 :(得分:0)
我相信有更简单的方法:
// Special available `%`
template <typename T, typename U>
constexpr auto modulo(T const& x, U const& y) -> decltype(x % y) {
return x % y;
}
注意:基于%
的检测,因此只要它们实现运算符,也适用于自定义类型。当我在它的时候,我也把它变成了混合型。
// Special floating point
inline constexpr float modulo(float x, float y) { return /*something*/; }
inline constexpr double modulo(double x, double y) { return /*something*/; }
inline constexpr long double modulo(long double x, long double y) { return /*something*/; }
注意:让fmod
可用更干净,遗憾的是我不相信constexpr
;因此,我选择为浮点类型设置非模板模,这允许您执行 magic 以根据类型的二进制表示来计算精确的模数。
答案 5 :(得分:0)
如果你愿意,你可以做得更简单:
template<typename A, typename B>
constexpr auto Modulo(const A& a, const B& b) -> decltype(a - (b * int(a/b)))
{
return a - (b * int(a/b));
}