std::complex 的模板函数重载

Template function overload for std::complex

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

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>>(或浮动)的重载工作?

使用标签分派 select 您想要的重载。此代码需要 C++14,但可以很容易地使其与 C++11 一起使用。但是你会失去可读性(重复的函数体,没有enable_if_t

首先,定义标签:

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

一些辅助模板(标签select离子,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;
};

}

并且每个重载+默认一个到select他们:

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

我想创建一个提取器助手(例如,从 std::complex<float> 中提取 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 而不是使用默认类型名称)。

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

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

您可以通过重载或使用带有静态成员函数的 class(可以部分特化)来解决这个问题。

通过函数参数推导,可以提取基础类型:

//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