6

我遇到了一些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>&)'
4

3 回答 3

11

这使用__typeof__GCC 扩展进行编译。看起来 GCCvalarray使用表达式模板来延迟窦的计算。但这会使sin模板的返回类型不完全正确valarray<T>,而是一些奇怪的复杂类型。

#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所示。它的工作原理是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)));
于 2009-08-30T12:53:54.070 回答
10

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]);
于 2009-08-30T12:52:05.567 回答
4

您在标题中谈到std::sin,然后分配::sin

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

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

编辑:不幸的是,你不走运。该标准说明sin(valarray<T> const &)了以下内容(26.3.3.3)。

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

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

于 2009-08-30T12:34:14.017 回答