Правильный способ ограничить функции определенными типами данных

В настоящее время я работаю со следующими типами данных в C ++:

double, boost :: interval< двойной> и
типы mpfr / mpfi boost (mpfr_float, mpfr_float_50, …, mpfi_float, mpfi_float_50, …)

Я пишу некоторые перегруженные функции для любой пары этих типов. Код идентичен за исключением типов в объявлении.

Количество пар довольно велико, и я хотел бы знать, как наиболее эффективно позаботиться обо всех этих случаях (использование шаблонов ванильной формы не идеально, потому что должны быть разрешены только эти типы чисел).

2

Решение

Вы можете посмотреть в 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";
}
1

Другие решения

Я обновил ответ, чтобы иметь дело со сложными / составными типами по вашему выбору.

Обработка известных числовых типов

Возможно, вам даже не важно, какая библиотека реализует типы:

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

Обратите внимание, что это также показывает один из подходов к синтезу универсального возвращаемого типа.

Жить на Колиру

#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 для пользовательских типов

Комплекс обработки или соединения

В этом случае нет существующей черты типа, на которую можно положиться. Кроме того, типы совершенно не связаны, поэтому не нужно добавлять одну частичную специализацию. В таких случаях я бы использовал пользовательскую черту.

Вы не очень подробно описываете тип выполняемой операции, поэтому позвольте мне остановиться на foo, foo-ную A а также B должно быть реализовано, если A а также B являются fooвозможно вместе.

Давайте сделаем черту, чтобы определить, какие типы 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 << "\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";
}

Печать

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))
2

По вопросам рекламы [email protected]