将函数限制为特定数据类型的正确方法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将函数限制为特定数据类型的正确方法相关的知识,希望对你有一定的参考价值。
我目前正在使用c ++中的以下数据类型:
double,boost :: interval <double>和boost的mpfr / mpfi类型(mpfr_float,mpfr_float_50,...,mpfi_float,mpfi_float_50,...)
我正在为这些类型的任何一对编写一些重载函数。除了声明中的类型之外,代码是相同的。
对的数量相当大,我想知道处理所有这些情况的最有效方法(使用vanilla形式的模板并不理想,因为只允许这些数字类型)。
你可以看看boost/multiprecision/mpfr.hpp
如何定义mpfr_float_...
。这些类型本身就是模板
typedef number<mpfr_float_backend<50> > mpfr_float_50;
typedef number<mpfr_float_backend<100> > mpfr_float_100;
typedef number<mpfr_float_backend<500> > mpfr_float_500;
typedef number<mpfr_float_backend<1000> > mpfr_float_1000;
typedef number<mpfr_float_backend<0> > mpfr_float;
基于这种观察,我们可以很容易地得到一个只匹配这些类型的模板:
#include <iostream>
#include <boost/multiprecision/mpfr.hpp>
using boost::multiprecision::number;
using boost::multiprecision::backends::mpfr_float_backend;
template < unsigned size >
void print(number<mpfr_float_backend<size>> const &num)
{
std::cout << "mpfr_float_" << size << ": " << num << "
";
}
int main()
{
using namespace boost::multiprecision;
mpfr_float_50 a = 1;
mpfr_float_100 b = 2;
mpfr_float_500 c = 3;
double d = 4;
print(a);
print(b);
print(c);
//print(d); // BOOM!
}
您当然也可以更通用一些,并允许任何Boost.Multiprecision类型:
template < typename... T >
void print(boost::multiprecision::number<T...> const &num)
{
std::cout << num << "
";
}
我已经更新了处理您选择的复杂/复合类型的答案。
处理已知的数字类型
也许您甚至不关心库实现哪些类型:
template <typename T, typename U>
std::enable_if_t<
std::numeric_limits<T>::is_specialized &&
std::numeric_limits<U>::is_specialized,
std::common_type_t<T, U>
> foo(T const& a, U const& b)
{
auto product = a * b;
std::cout << "foo(" << a << ", " << b << ") -> " << product << "
";
return product;
}
请注意,这也显示了一种合成通用返回类型的方法。
#include <iostream>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/mpfr.hpp>
#include <boost/multiprecision/number.hpp>
template <typename T, typename U>
std::enable_if_t<
std::numeric_limits<T>::is_specialized &&
std::numeric_limits<U>::is_specialized,
std::common_type_t<T, U>
> foo(T const& a, U const& b)
{
auto product = a * b;
std::cout << "foo(" << a << ", " << b << ") -> " << product << "
";
return product;
}
int main()
{
using namespace boost::multiprecision;
mpfr_float_50 a = 1;
mpfr_float_100 b = 2;
mpfr_float_500 c = 3;
double d = 4;
foo(a, a); foo(b, a); foo(c, a); foo(d, a); std::cout << "
";
foo(a, b); foo(b, b); foo(c, b); foo(d, b); std::cout << "
";
foo(a, c); foo(b, c); foo(c, c); foo(d, c); std::cout << "
";
foo(a, d); foo(b, d); foo(c, d); foo(d, d);
}
打印
foo(1, 1) -> 1
foo(2, 1) -> 2
foo(3, 1) -> 3
foo(4, 1) -> 4
foo(1, 2) -> 2
foo(2, 2) -> 4
foo(3, 2) -> 6
foo(4, 2) -> 8
foo(1, 3) -> 3
foo(2, 3) -> 6
foo(3, 3) -> 9
foo(4, 3) -> 12
foo(1, 4) -> 4
foo(2, 4) -> 8
foo(3, 4) -> 12
foo(4, 4) -> 16
注意:这里的弱点是
common_type
,因为它可能不知道您的库类型,因此无法建议正确的返回类型。见std::common_type trait for user defined types
处理复合物或化合物
在这种情况下,没有可依赖的现有类型特征。此外,类型完全不相关,因此不需要添加单个部分特化。在这种情况下,我会使用自定义特征。
你没有详细描述你将要实现的操作类型,所以让我坚持使用
foo
。如果foo
和A
是B
-able,那么应该实施A
-ingB
和foo
。
让我们做一个特性来定义哪些类型是可以实现的。如果foo
是可交换的,那么你只需要一个“边”:
#include <type_traits>
namespace mylib { namespace traits {
//primary template
template <typename T, typename Enable = void> struct is_fooable : std::false_type {};
// c++14 style
template <typename T> constexpr bool is_fooable_v = is_fooable<T>::value;
} }
然后,您可以专注于您希望支持的任何类型:
namespace mylib {
namespace traits {
template <typename Backend, boost::multiprecision::expression_template_option Et>
struct is_fooable<boost::multiprecision::number<Backend, Et> >
: std::true_type {};
template <typename T>
struct is_fooable<T, std::enable_if_t<std::is_arithmetic<T>{}> >
: std::true_type {};
template <typename T, typename Policies>
struct is_fooable<boost::numeric::interval<T, Policies> >
: std::true_type {};
template <typename T>
struct is_fooable<std::complex<T> >
: std::true_type {};
}
template <typename T, typename U, typename = std::enable_if_t<traits::is_fooable_v<T> && traits::is_fooable_v<U> > >
auto foo(T const& a, U const& b) {
auto product = a * b;
std::cout << "foo(" << a << ", " << b << ") -> " << product << "
";
return product;
}
}
Full Demo
#include <type_traits>
namespace mylib { namespace traits {
//primary template
template <typename T, typename Enable = void> struct is_fooable : std::false_type {};
// c++14 style
template <typename T> constexpr bool is_fooable_v = is_fooable<T>::value;
} }
#include <iostream>
#include <sstream>
#include <complex>
#include <boost/numeric/interval.hpp>
#include <boost/numeric/interval/io.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/mpfr.hpp>
#include <boost/multiprecision/number.hpp>
namespace mylib {
namespace traits {
template <typename Backend, boost::multiprecision::expression_template_option Et>
struct is_fooable<boost::multiprecision::number<Backend, Et> >
: std::true_type {};
template <typename T>
struct is_fooable<T, std::enable_if_t<std::is_arithmetic<T>{}> >
: std::true_type {};
template <typename T, typename Policies>
struct is_fooable<boost::numeric::interval<T, Policies> >
: std::true_type {};
template <typename T>
struct is_fooable<std::complex<T> >
: std::true_type {};
}
template <typename T, typename U, typename = std::enable_if_t<traits::is_fooable_v<T> && traits::is_fooable_v<U> > >
auto foo(T const& a, U const& b) {
std::ostringstream oss;
oss << "foo(" << a << ", " << b << ")";
return oss.str();
}
}
int main()
{
using namespace boost::multiprecision;
mpfr_float_50 a = 1;
float c = 3;
std::complex<double> d(4, 1);
boost::numeric::interval<int> b(1,1);
using mylib::foo;
std::cout << foo(a, a) << "; " << foo(b, a) << "; " << foo(c, a) << "; " << foo(d, a) << "
";
std::cout << foo(a, b) << "; " << foo(b, b) << "; " << foo(c, b) << "; " << foo(d, b) << "
";
以上是关于将函数限制为特定数据类型的正确方法的主要内容,如果未能解决你的问题,请参考以下文章