std :: complex的模板函数重载

时间:2016-10-15 19:02:33

标签: c++ c++11 templates sfinae complex-numbers

我有一个为我的数组生成随机数的函数。我为浮点数和整数创建了两个重载,如下所示:

template <typename T, int M = 0>
typename std::enable_if<std::is_floating_point<T>::value && std::is_scalar<T>::value,MyArray<T,M> >::type
Random()
{
    //...
}

template <typename T, int M = 0>
typename std::enable_if<std::is_integral<T>::value && std::is_scalar<T>::value, MyArray<T,M> >::type
Random()
{
    //...
}

可以使用以下方法调用这些函数:

MyArray<int> i    = Random<int>();
MyArray<double> d = Random<double>();

我希望实现相同但std::complex<T>,其中T可以是任何浮点类型(最初是double和float足够好)。我希望能够做到这一点:

//This is what I'm trying to achieve
MyArray<std::complex<double> > = Random<std::complex<double>>();
MyArray<std::complex<float > > = Random<std::complex<float >>();

我无法完全实现这一目标,但却能够实现:

Random<std::complex,double>()

使用模板模板参数,这不是我正在寻找的。

如何通过<std::complex<double>>(或浮点数)来实现重载?

4 个答案:

答案 0 :(得分:3)

使用标签分派选择所需的过载。这段代码需要C ++ 14,但它可以很容易地与C ++ 11一起使用。尽管如此(重复的函数体,没有enable_if_t

,你会失去可读性

首先,定义标签:

struct integral_tag{};
struct floating_point_tag{};
struct complex_tag{};
struct error_tag{};

一些助手模板(标签选择,complex检测):

namespace detail
{

template<typename T> struct is_complex : false_type {};
template<typename T> struct is_complex<complex<T>> : true_type {};

template<typename T, typename = void>
struct select { using type = error_tag; };

template<typename T>
struct select<T, enable_if_t<is_integral<T>::value>>{
    using type = integral_tag;
};

template<typename T>
struct select<T, enable_if_t<is_floating_point<T>::value>>{
    using type = floating_point_tag;
};

template<typename T>
struct select<T, enable_if_t<is_complex<T>::value>>{
    using type = complex_tag;
};

}

和每个重载+默认选择它们:

template<typename T>
using random_tag = typename detail::select<T>::type;

template<typename T>
auto Random(floating_point_tag){
    return T{};
}

template<typename T>
auto Random(integral_tag){
    return T{};
}

template<typename T>
auto Random(complex_tag){
    return T{typename T::value_type{}, typename T::value_type{}+1};
}

template<typename T>
auto Random()
{
    return Random<T>(random_tag<T>{});
}

然后你可以使用它们:

int main()
{
    cout << Random<int>() << endl;
    cout << Random<float>() << endl;
    cout << Random<complex<double>>() << endl;
}

live demo

答案 1 :(得分:2)

我认为创建一个提取器帮助器(例如,从float中提取std::complex<float>)为

template <typename T>
struct extractType;

template <template <typename ...> class C, typename D>
struct extractType<C<D>>
 { using subType = D; };

你可以使用它并写

template <typename T, int M = 0>
typename std::enable_if<std::is_same<T, std::complex<
               typename extractType<T>::subType>>::value
            && std::is_floating_point<typename extractType<T>::subType>::value
            && std::is_scalar<typename extractType<T>::subType>::value,
         MyArray<T,M> >::type
Random ()
 { return MyArray<T,M>{}; }

或只是

template <typename T, int M = 0>
typename std::enable_if<std::is_same<T,
            std::complex<typename extractType<T>::subType>>::value,
            MyArray<T,M> >::type
Random ()
 { return MyArray<T,M>{}; }

如果您可以明显地认为std::complex的模板参数是浮动和标量的。

---编辑---

添加了一个完整的示例(嗯......带有错误的MyArray

#include <array>
#include <complex>
#include <type_traits>

template <typename T, int M = 0>
using MyArray = std::array<T, 10U>;


template <typename T>
struct extractType;

template <template <typename ...> class C, typename D>
struct extractType<C<D>>
 { using subType = D; };


template <typename T, int M = 0>
typename std::enable_if<std::is_floating_point<T>::value && std::is_scalar<T>::value,MyArray<T,M> >::type
Random ()
 { return MyArray<T,M>{}; }

template <typename T, int M = 0>
typename std::enable_if<std::is_integral<T>::value && std::is_scalar<T>::value, MyArray<T,M> >::type
Random ()
 { return MyArray<T,M>{}; }

template <typename T, int M = 0>
typename std::enable_if<std::is_same<T,
            std::complex<typename extractType<T>::subType>>::value,
            MyArray<T,M> >::type
Random ()
 { return MyArray<T,M>{}; }



int main()
 {
   MyArray<int>                 i = Random<int>();
   MyArray<double>              d = Random<double>();
   MyArray<std::complex<float>> c = Random<std::complex<float>>();
 }

- 编辑2 -

根据OP改进修改的解决方案和示例(直接使用extractType<T>::subType而不是使用默认类型名称。)

答案 2 :(得分:1)

只需将新功能基于您已有的功能:

template <>
auto Random<std::complex<double> >()
{
    return std::complex<double>{Random<double>(), Random<double>()};
}

由于不允许对功能模板进行部分专业化,因此不能在这里完全通用并使用

template <typename T>
auto Random<std::complex<T> >() { ... }   //error: partial specialization not allowed

您可以使用静态成员函数重载或使用类(可以部分专门化)来解决这个问题。

答案 3 :(得分:0)

使用函数参数推导可以提取基础类型:

//overload for templates i.e. std::complex
template <class R, template <class...> class T> 
R underlying_t_f(T<R>);
//overload for non template types
template < class T>
 T underlying_t_f(T);

template < class T>
using underlying_t = decltype(underlying_t_f(T()));

然后在您的代码中将T替换为underlying_t<T>Online Demo