【问题标题】:Creating a default function parameter with templates使用模板创建默认函数参数
【发布时间】:2021-04-06 09:47:49
【问题描述】:

我正在尝试制作一种方法,如果可用,用户可以提供分析梯度函数,但如果没有,则默认使用数值梯度。

为此,我尝试了以下方法:

template<typename V, typename S, S (*Fun)(const V&)> V NumericGradient(const V& p)
{
    const S e = 1.0e-6;
    return {
        Fun(p + V(e, 0, 0)) - Fun(p - V(e, 0, 0)) / S(2) * e,
        Fun(p + V(0, e, 0)) - Fun(p - V(0, e, 0)) / S(2) * e,
        Fun(p + V(0, 0, e)) - Fun(p - V(0, 0, e)) / S(2) * e,
    };
}

template<typename V, typename S = decltype(V()[0])>
std::vector<V> DualContouring(
    S (*DistanceFunction)(const V&),
    V (*GradientFunction)(const V&) = NumericGradient<V, S, decltype(DistanceFunction)>,
    const S span = S(50),
    const unsigned int resolution = 100)
{
    return {};
}

但是编译器失败并显示此消息(在实例化之后):

terrain_generation.cpp
../Src/examples/TerrainGeneration/terrain_generation.cpp: In function ‘std::vector<_RealType> DualContouring(S (*)(const V&), V (*)(const V&), S, unsigned int) [with V = Eigen::Matrix<float, 3, 1>; S = float]’:
../Src/examples/TerrainGeneration/terrain_generation.cpp:362:63: error: no matches converting function ‘NumericGradient’ to type ‘class Eigen::Matrix<float, 3, 1> (*)(const class Eigen::Matrix<float, 3, 1>&)’
  362 |         [](const Eigen::Vector3f& v) { return v.dot(v) - 10; });
      |                                                               ^
In file included from ../Src/Engine/Renderer/Renderer.hpp:24,
                 from ../Src/examples/TerrainGeneration/terrain_generation.cpp:32:
../Src/Engine/Geometry/DualContouring.hpp:97:56: note: candidate is: ‘template<class V, class S, S (* Fun)(const V&)> V NumericGradient(const V&)’
   97 | template<typename V, typename S, S (*Fun)(const V&)> V NumericGradient(const V& p)
      |                                                        ^~~~~~~~~~~~~~~
../Src/examples/TerrainGeneration/terrain_generation.cpp:362:63: note:   when instantiating default argument for call to ‘std::vector<_RealType> DualContouring(S (*)(const V&), V (*)(const V&), S, unsigned int) [with V = Eigen::Matrix<float, 3, 1>; S = float]’
  362 |         [](const Eigen::Vector3f& v) { return v.dot(v) - 10; });
      |                                                               ^
../Src/examples/TerrainGeneration/terrain_generation.cpp: In function ‘void GenerateTerrain(Gallery&)’:
../Src/examples/TerrainGeneration/terrain_generation.cpp:362:63: error: no matches converting function ‘NumericGradient’ to type ‘class Eigen::Matrix<float, 3, 1> (*)(const class Eigen::Matrix<float, 3, 1>&)’
In file included from ../Src/Engine/Renderer/Renderer.hpp:24,
                 from ../Src/examples/TerrainGeneration/terrain_generation.cpp:32:
../Src/Engine/Geometry/DualContouring.hpp:97:56: note: candidate is: ‘template<class V, class S, S (* Fun)(const V&)> V NumericGradient(const V&)’
   97 | template<typename V, typename S, S (*Fun)(const V&)> V NumericGradient(const V& p)
      |                                                        ^~~~~~~~~~~~~~~
make[1]: *** [TerrainGeneration.make:156: obj/Debug/TerrainGeneration/terrain_generation.o] Error 1
make: *** [Makefile:39: TerrainGeneration] Error 2
make: Leaving directory '/home/makogan/vkengine/Generated'
Traceback (most recent call last):
  File "build.py", line 139, in <module>
    main()
  File "build.py", line 106, in main
    raise Exception("Compilation failed.")
Exception: Compilation failed.

我不确定那里的类型定义有什么问题。 NumericGradient 的实例化应该与预期的函数类型兼容。

这就是我尝试调用存根的方式:

auto contour = DualContouring<Eigen::Vector3f, float>(
        [](const Eigen::Vector3f& v) { return v.dot(v) - 10; });

我在linux下用g++ 9.3编译。

替代问题

好的,我尝试完全删除默认参数并强制用户始终传递函数指针。

不知何故:

    float (*Distance)(const Eigen::Vector3f&) = [](const Eigen::Vector3f& v) {
        return v.dot(v) - 9;
    };
    auto contour = DualContouring<Eigen::Vector3f, float>(
        Distance, NumericGradient<Eigen::Vector3f, float>, Distance);

编译失败并出现错误:

error: no matches converting function ‘NumericGradient’ to type ‘class Eigen::Matrix<float, 3, 1> (*)(const class Eigen::Matrix<float, 3, 1>&)’
  365 |         Distance, NumericGradient<Eigen::Vector3f, float>, Distance);```

Why? The template declaration should be fine.

【问题讨论】:

  • ~能否给我们一个 DualContouring 的使用示例(带示例中使用的类型的定义)?~ Duh, V = Eigen::Matrix ; S = 浮动...
  • 如果你提到你正在使用的编译器(w/版本)也会很有帮助。
  • @PedroBoechat 我添加了请求的信息。

标签: c++ templates compiler-errors template-meta-programming


【解决方案1】:

Jarod42 explanation 之后,NumericGradient 的第三个模板参数是一个非类型参数,decltype(DistanceFunction) 是一个类型。所以,我相信你想做的是使用DistanceFunction,但是,正如他所提到的,参数名称不能在这种情况下使用。

作为替代方案,您可以将函数指针模板参数“降级”为常规函数指针参数:

template<typename V, typename S> V NumericGradient(const V& p, S (*Fun)(const V&));

并传递它。

这是一个工作示例: https://godbolt.org/z/qhY938

作为另一种选择,您可以将DistanceFunction 作为模板参数传递给DualContouring

这是另一个工作示例: https://godbolt.org/z/EnaMjK

【讨论】:

  • 将函数降级为常规指针的问题在于,它现在强制函数的签名采用 2 个参数而不是一个参数,这会弄乱我正在尝试设计的接口。
  • 也许您可以将 DistanceFunction 作为模板参数传递给 DualContouring,然后呢?但是你不能再使用 lambda 了。我会用那个替代方案更新我的答案。
  • 我也试过了,它涉及在第二个函数中调用第一个函数,这显然是不允许的。即您不能在签名上的 lambda 内调用模板参数。
  • 这样的东西不能满足您的需求:godbolt.org/z/EnaMjK?
  • 我突然想到,也许您可​​能想使用 std::bind() 将 DistanceFunction 绑定到 GradientFunction,然后将返回的可调用对象作为 GradientFunction 传递给 DualContouring。这样您就不需要从 DualCountouring 内部将距离函数传递给 GradientFunction。
【解决方案2】:

NumericGradient&lt;V, S, decltype(DistanceFunction)&gt; 是错误的,因为第三个参数应该是函数指针(一个值),而您传递的是一个类型。

应该是NumericGradient&lt;V, S, DistanceFunction&gt;,但是..DistanceFunction 不是常量表达式。

【讨论】:

  • 为什么不是常量表达式?不是在编译时就确定了吗?
  • 函数参数不是常量表达式。
猜你喜欢
  • 1970-01-01
  • 2013-09-29
  • 2011-06-11
  • 2021-11-01
  • 2017-09-16
  • 1970-01-01
  • 2015-07-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多