【问题标题】:Template function overload for std::complexstd::complex 的模板函数重载
【发布时间】:2016-10-15 19:02:33
【问题描述】:

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

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&lt;T&gt;,其中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>()

使用模板模板参数,这不是我要找的。​​p>

如何让&lt;std::complex&lt;double&gt;&gt;(或浮点数)的重载工作?

【问题讨论】:

  • std::is_same>::value 不起作用?
  • @DeiDei 这怎么可能?这看起来是递归的!反正我会试试的。
  • @DeiDei Nope... 不起作用。它说重载MyArray&lt;std::complex&lt;double&gt; &gt;没有匹配函数。
  • template&lt;typename T, int M = 0&gt; typename std::enable_if&lt;std::is_same&lt;T, complex&lt;double&gt; &gt;::value, MyArray&lt;complex&lt;double&gt;,M&gt; &gt;::type你确定吗?
  • @DeiDei 我不想专攻双倍。我希望检测到double。如果它是浮动的呢?我不想重写float函数。

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


【解决方案1】:

使用标签调度来选择你想要的重载。此代码需要 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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 如果您可以在ideone.com 中创建一个最小示例,那就太好了。我正在尝试实现这个(我不得不说,它看起来真的很花哨),但我仍然得到“没有匹配的函数或调用......”。
    • @TheQuantumPhysicist - 添加了一个完整的例子;用我的 clang++ 和 g++ 编译;希望这会有所帮助
    • 出于某种非常奇怪的原因...在我的代码中,D 被猜测为int...我不知道为什么!您的代码有效,extractType 的一个简单示例有效,但我的代码对类型做出了错误的猜测...有趣的是,如果我输入Random&lt;std::complex&lt;double&gt;,0,double&gt;,它会有效...如果我删除最后一个double ,它不起作用......有什么想法吗?
    • 更有趣...如果我这样做Random&lt;std::complex&lt;double&gt;,0,extractType&lt;std::complex&lt;double&gt;::SubType&gt;,它会起作用... g++ 正在积极做出错误的猜测...
    • @TheQuantumPhysicist - 不错的改进; krzaq 是对的:使用默认类型进行演绎有点危险;修改了我的答案以吸收您的改进
    【解决方案3】:

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

    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
    

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

    【讨论】:

    • 我喜欢这背后的想法,但需要为float 编写不同的函数似乎有点多余。整数类型也是如此。但是,即使为 OPs 编写重载,生成的代码也可能少于标记调度或类专业化方法(并且更易于阅读)
    【解决方案4】:

    通过函数参数推导可以提取底层类型:

    //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&lt;T&gt;Online Demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-08-22
      • 1970-01-01
      • 2021-07-15
      • 2019-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-04
      相关资源
      最近更新 更多