将函数限制为特定数据类型的正确方法

时间:2017-12-16 01:34:55

标签: c++ boost mpfr mpfi

我目前正在使用c ++中的以下数据类型:

double,boost :: interval<双>,和 提升mpfr / mpfi类型(mpfr_float,mpfr_float_50,...,mpfi_float,mpfi_float_50,...)

我正在为这些类型的任何一对编写一些重载函数。除了声明中的类型之外,代码是相同的。

对的数量相当大,我想知道处理所有这些情况的最有效方法(使用香草形式的模板并不理想,因为只允许这些数字类型)

2 个答案:

答案 0 :(得分:2)

我已经更新了处理您选择的复杂/复合类型的答案。

处理已知数字类型

也许您甚至不关心库实现哪些类型:

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 << "\n";
    return product;
}

注意,这也显示了一种合成通用返回类型的方法。

<强> Live On Coliru

#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 << "\n";
    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 << "\n";
    foo(a, b); foo(b, b); foo(c, b); foo(d, b); std::cout << "\n";
    foo(a, c); foo(b, c); foo(c, c); foo(d, c); std::cout << "\n";
    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。如果fooA BA,那么B foofoo应该得到实施。

让我们做一个特性来定义哪些类型是可以实现的。如果#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; } } 是可交换的,则只需要一个&#34; side&#34;:

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 << "\n";
        return product;
    }
}

然后,您可以专注于您希望支持的任何类型:

#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) << "\n";
    std::cout << foo(a, b) << "; " << foo(b, b) << "; " << foo(c, b) << "; " << foo(d, b) << "\n";
    std::cout << foo(a, c) << "; " << foo(b, c) << "; " << foo(c, c) << "; " << foo(d, c) << "\n";
    std::cout << foo(a, d) << "; " << foo(b, d) << "; " << foo(c, d) << "; " << foo(d, d) << "\n";
}

完整演示

<强> Live On Coliru

foo(1, 1); foo([1,1], 1); foo(3, 1); foo((4,1), 1)
foo(1, [1,1]); foo([1,1], [1,1]); foo(3, [1,1]); foo((4,1), [1,1])
foo(1, 3); foo([1,1], 3); foo(3, 3); foo((4,1), 3)
foo(1, (4,1)); foo([1,1], (4,1)); foo(3, (4,1)); foo((4,1), (4,1))

打印

import math
import numpy as np
import itertools
from numba import jit
from sympy.solvers import solve
from sympy import Symbol
from sympy import Poly

@jit
def polyn(ranges=[[-20,20],[-20,20],[-20,20],[-20,20]],step=4):
    l = []
    x = Symbol('x')
    rangl = [np.linspace(i[0],i[1],math.floor((i[1]-i[0])/step)) for i in ranges]
    coeffl = iter(itertools.product(*rangl))
    leng = 1
    for i in rangl:
        leng *= len(i)
    for i in range(0, leng):
        a = solve(Poly(list(next(coeffl)),x),x)
        for j in a:
            l.append(j)
    return np.array(l)

答案 1 :(得分:1)

您可以查看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 << "\n";
}


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 << "\n";
}