【问题标题】:Using auto type'd lambdas with templated functions - using automatic template deduction with g++ 5.4使用带有模板函数的自动类型化 lambdas - 使用 g++ 5.4 的自动模板推导
【发布时间】:2018-01-30 01:44:05
【问题描述】:

我正在与编译器及其使用自动模板推导的能力作斗争。以下不编译,模板自动推演失败:

template<typename Configuration, typename ConfigAlloc = std::allocator<Configuration>>
std::vector<Configuration, ConfigAlloc> ResamplePath(
        const std::vector<Configuration, ConfigAlloc>& path,
        const double resampled_state_distance,
        const std::function<double(const Configuration&, const Configuration&)>& state_distance_fn,
        const std::function<Configuration(const Configuration&, const Configuration&, const double)>& state_interpolation_fn)
{
    ...
}

void foo()
{
    std::vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d>> path;
    ...

    const auto interpolation_fn = [&] (
            const Eigen::Vector3d& prev,
            const Eigen::Vector3d& curr,
            const double ratio)
    {
        Eigen::Vector3d interpolated;
        ...
        return interpolated;
    };

    auto resample_result = ResamplePath(path, ..., interpolation_fn);
}

如果我将 lambda 声明更改为

    const std::function<Eigen::Vector3d(const Eigen::Vector3d&, const Eigen::Vector3d&, const double)> interpolation_fn = [&] (
            const Eigen::Vector3d& prev,
            const Eigen::Vector3d& curr,
            const double ratio)
    {
        Eigen::Vector3d interpolated;
        ...
        return interpolated;
    };

或者我更改函数调用以显式定义类型

auto resample_result = ResamplePath<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d>>(
                path, ..., interpolation_fn);

然后一切编译没有任何问题。

我是否遗漏了一些可能允许自动推导在不明确定义 lambda 类型的情况下工作的东西?

编辑:准确的编译错误

virtual_rubber_band.cpp: In member function ‘void smmap::VirtualRubberBand::resampleBand(bool)’:
virtual_rubber_band.cpp:279:111: error: no matching function for call to ‘ResamplePath(EigenHelpers::VectorVector3d&, const double&, const smmap::VirtualRubberBand::resampleBand(bool)::<lambda(const Vector3d&, const Vector3d&)>&, const smmap::VirtualRubberBand::resampleBand(bool)::<lambda(const Vector3d&, const Vector3d&, double)>&)’
                 band_, max_distance_between_rubber_band_points_, state_distance_fn, state_interpolation_fn);
In file included from virtual_rubber_band.cpp:3:0:
shortcut_smoothing.hpp:191:52: note: candidate: template<class Configuration, class ConfigAlloc> std::vector<_Tp, _Alloc> shortcut_smoothing::ResamplePath(const std::vector<_Tp, _Alloc>&, double, const std::function<double(const Datatype&, const Datatype&)>&, const std::function<Configuration(const Configuration&, const Configuration&, double)>&)
 inline std::vector<Configuration, ConfigAlloc> ResamplePath(
                                                ^
shortcut_smoothing.hpp:191:52: note:   template argument deduction/substitution failed:
virtual_rubber_band.cpp:279:111: note:   ‘const smmap::VirtualRubberBand::resampleBand(bool)::<lambda(const Vector3d&, const Vector3d&)>’ is not derived from ‘const std::function<double(const Datatype&, const Datatype&)>’
                 band_, max_distance_between_rubber_band_points_, state_distance_fn, state_interpolation_fn);

【问题讨论】:

  • »以下无法编译,模板自动推演失败«这是一个必须猜错的游戏吗?
  • 寻求调试帮助的问题(“为什么这段代码不起作用?”)必须包括所需的行为、特定问题或错误在问题本身中重现它所需的最短代码。没有明确的问题陈述的问题对其他读者没有用处。请参阅:How to create a Minimal, Complete, and Verifiable example
  • 我试图创建一个最小的、完整的、可验证的示例,但是这样的示例很难创建,因为它们位于不同的库中;我能够创建的最小示例是大约 2000 行代码。我正在按要求添加确切的编译错误。

标签: c++ c++11 templates lambda g++


【解决方案1】:

lambda 不是标准函数

std 函数是类型橡皮擦。它会擦除除“签名调用”、复制和销毁(以及不重要​​的内容)之外的所有内容。

类型推导和类型擦除是相反的操作。推断类型擦除类型是代码异味。

template<typename Configuration, typename ConfigAlloc = std::allocator<Configuration>,
  class Distance, class Interpolate>
std::vector<Configuration, ConfigAlloc> ResamplePath(
    const std::vector<Configuration, ConfigAlloc>& path,
    const double resampled_state_distance,
    const Distance& state_distance_fn,
    const Interpolate& state_interpolation_fn)

现在我们摆脱了试图推断如何键入擦除,并且......停止键入擦除。这可以提高运行时性能,因为编译器发现内联超过类型擦除很棘手。

template<typename Configuration, typename ConfigAlloc = std::allocator<Configuration>>
std::vector<Configuration, ConfigAlloc> ResamplePath(
    const std::vector<Configuration, ConfigAlloc>& path,
    const double resampled_state_distance,
    block_deduction<const std::function<double(const Configuration&, const Configuration&)>&> state_distance_fn,
    block_deduction<const std::function<Configuration(const Configuration&, const Configuration&, const double)>&> state_interpolation_fn)

其中block_deduction 阻止编译器尝试使用这些参数进行推断。

template<class T>struct tag_t{using type=T;};
template<class T>using block_deduction=typename tag_t<T>::type;

你真正的问题可能是你想使用std::function 作为一个概念——你希望参数与那个签名兼容——这还不是 C++ 的一部分。概念可能会到达

【讨论】:

  • 所以假设我不能更改函数原型,听起来我最好的选择可能是像我所做的那样显式声明模板参数?我也许可以更改函数原型,但此时需要更长的过程。
  • This post 可能会帮助人们获得有关 Yakk 第一个决议的更多详细信息:
  • @bitz 如果您有无法修复的损坏代码,请编写一个包装器;任一选项都可用于包装原件。
  • block_deduction 技术如何与编译器无关?特别是我担心clang vs. g++ vs MSVC。这种类型的模板元编程我不熟悉。
  • @bitz 完全
猜你喜欢
  • 2021-11-24
  • 1970-01-01
  • 1970-01-01
  • 2019-11-29
  • 2018-05-01
  • 1970-01-01
  • 2022-01-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多