【问题标题】:How do I refer to std::sin(const valarray<double> &)?如何引用 std::sin(const valarray<double> &)?
【发布时间】:2010-11-24 03:16:09
【问题描述】:

我遇到了一些 valarray 函数指针代码的问题:

double (*fp)(double) = sin;
valarray<double> (*fp)(const valarray<double> &) = sin;

第一个编译,第二个给出:

error: no matches converting function 'sin' to type 'class std::valarray<double> (*)(const class std::valarray<double>&)'

【问题讨论】:

    标签: c++ function-pointers functional-programming


    【解决方案1】:

    你在标题中提到std::sin,然后指定::sin

    valarray<double> (*fp)(const valarray<double> &) = std::sin;
    

    应该可以。请注意,您应该限定所有 sin 的使用,尽管大多数实现会将名称注入全局命名空间,即使您包含 &lt;cmath&gt;(这是非标准行为)。

    编辑:不幸的是,你运气不好。该标准对sin(valarray&lt;T&gt; const &amp;) 进行了以下说明(26.3.3.3)。

    这个函数应该返回一个 T 类型的值或者可以是明确的 转换为 T 型。

    gcc 执行的优化是由标准授予的。上面的代码不保证能正常工作。

    【讨论】:

    • 你是对的,在 GCC 上它确实失败了。我会说这是一个错误。该标准明确定义了template&lt;class T&gt; valarray&lt;T&gt; sin (const valarray&lt;T&gt;&amp;);,它当然可以转换为valarray&lt;double&gt; (*)(const valarray&lt;double&gt; &amp;)
    • 我同意。看起来像海湾合作委员会的疯狂:(
    【解决方案2】:

    26 3.1/3

    任何返回 valarray 的函数都可以返回另一种类型的对象, 前提是 valarray 的所有 const 成员函数也适用于该类型。

    目的是允许使用模板表达式来优化结果(即在整个数组上循环一次,每次计算,直接分配给结果 valarray 而不是构建一个临时的)。

    z = sin(x+y);
    

    可以优化为

    for (i = 0; i < N; ++i)
       z[i] = sin(x[i] + y[i]);
    

    【讨论】:

      【解决方案3】:

      使用__typeof__ GCC 扩展进行编译。看起来 GCC 的 valarray 使用表达式模板来延迟计算窦。但这会使sin 模板的返回类型不完全是valarray&lt;T&gt;,而是一些奇怪的复杂类型。

      #include <valarray>
      
      template<typename T> struct id { typedef T type; };
      int main() {
        using std::valarray;
        using std::sin;
      
        id<__typeof__(sin(valarray<double>()))>::type (*fp)(const valarray<double> &) = sin;
      }
      

      编辑:请参阅 AProgrammer 的标准报价,了解为什么 GCC 可以这样做。

      编辑:符合标准的解决方法

      在没有__typeof__ 的情况下以严格符合标准的方式执行此操作有点棘手。您将需要获取sin 的返回类型。您可以为此使用条件运算符,例如 Eric Niebler has shown。它的工作原理是让sin 函数实际上没有被调用,而只是类型检查。通过尝试将条件运算符的另一个分支(实际计算的分支)转换为相同的类型,我们可以生成一个虚拟参数,以便能够推断出函数指针的类型:

      #include <valarray>
      
      using std::valarray;
      
      template<typename T> struct id {
        typedef T type;
      };
      
      struct ded_ty {
        template<typename T>
        operator id<T>() { return id<T>(); }
      };
      
      template<typename E, typename T>
      id<T(*)(valarray<E> const&)> genFTy(T t) { 
        return id<T(*)(valarray<E> const&)>(); 
      }
      
      template<typename T>
      void work(T fp, id<T>) {
        // T is the function pointer type, fp points
        // to the math function.
      }
      
      int main() {
        work(std::sin, 1 ? ded_ty() : genFTy<double>(std::sin(valarray<double>())));
      }
      

      如果你想马上得到地址,你可以写work让它再次返回fp

      template<typename T>
      T addy(T fp, id<T>) { return fp; }
      

      现在,您终于可以编写一个宏来封装条件运算符的诡计,并在您想要获取任何此类数学函数的地址时使用它。

      #define DEDUCE(FN,Y) (1 ? ded_ty() : genFTy<Y>(FN(std::valarray<Y>())))
      

      要获取地址并将其传递给某个通用函数,则可以使用以下方法

      std::transform(v1.begin(), v1.end(), v1.begin(),
        addy(std::sin, DEDUCE(std::sin, double)));
      std::transform(v2.begin(), v2.end(), v2.begin(),
        addy(std::cos, DEDUCE(std::cos, double)));
      

      【讨论】:

      • 我同时对您类似 MacGyver 的能力印象深刻,该能力将条件运算符转变为该问题的解决方案,并且对有必要这样做感到震惊。 :)
      • @robert,如果有不明白的部分,请随时提问。到时候我会试着给他们一个解释。还请务必在此处阅读 eric nieblers 对他使用 operator? 的解释:artima.com/cppsource/foreach2.html
      • 第一个变体也应该适用于所有已经支持 C++11 decltype 的编译器(在将 __typeof__ 替换为 decltype 之后)
      猜你喜欢
      • 2017-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-05
      • 1970-01-01
      相关资源
      最近更新 更多