【问题标题】:C++17 Template argument deduction failedC++17 模板参数推导失败
【发布时间】:2020-10-29 04:09:16
【问题描述】:

我想模拟一个计时器,它会定期调用一个函数(回调),因为我在 sn-p 之后编写了这个函数(虽然很天真),其中, 主函数中的 Timer//.. 行的参数推导失败。我正在用 c++17 std 编译这段代码。我该如何解决这个问题?还是那些必要的论点? 感谢您提供这方面的任何帮助。

#include <functional>
#include <chrono>

using namespace std::chrono_literals;

template<typename ...Args>
class Timer
{
public:
  Timer(std::function<void(Args...)> func, Args&&... args, std::chrono::milliseconds step)
    : m_callback{ func },
      m_args{ std::forward<Args>(args)... },
      m_time{ step },
      m_stop{ false },
  { }

private:
  std::function<void(Args...)> m_callback;
  std::thread m_executer;
  std::tuple<Args...> m_args;
  std::chrono::milliseconds m_time;
  bool m_stop;
};

int main()
{
  // Create a timer
  auto timer = Timer/*<int, int, float>*/([](int a, int b, float c) { }, 15, 17, 12.0f, 500ms);

  return 0;
}

【问题讨论】:

  • 我认为它失败了,因为第一个参数是 lambda 而不是 std::function。

标签: c++ c++11 c++17


【解决方案1】:

这里的模板参数推导有 2 个问题。 std::function 参数不能从 lambda 参数中推导出来,并且推导仅适用于尾随的参数包。

要解决此问题,您可以更改构造函数的参数顺序,如下所示:

Timer(std::function<void(Args...)> func, std::chrono::milliseconds step, Args&&... args)
 // ...                                                              //  ^^^^^^^^^ trailing 

并添加扣除指南

template<typename Func, typename ...Args>
Timer(Func, std::chrono::milliseconds, Args&&...) -> Timer<Args...>;

这是demo。

注意警告,您的成员初始化器列表与成员声明的顺序不同。

【讨论】:

  • 谢谢,我想念演绎指南。另外,谢谢我会更正初始化列表顺序
【解决方案2】:

我删除了与您的问题无关的所有内容(您应该这样做以便更容易获得帮助),这就是我要做的工作。首先,我移动了可变参数之前的毫秒数。它会使编译器混淆参数包的结束位置,因此您通常希望 ... 成为参数列表中的最后一件事。

其次,CTAD(类模板参数推导)不适合您,因为需要在匹配之前将您的 lambda 转换为 std::function。

如果 main() 按预期将实际的 std::function 传递给您的构造函数,则它可以工作:

auto timer = Timer(
    std::function<void(int, int, float)>([](int a, int b, float c) { }),
    500ms, 15, 17, 12.0f);

但这可以说比你最初尝试的更糟糕。

更好的解决方法是帮助 CTAD 弄清楚你在用 c++17 做什么演绎指南。这是一个模式匹配,“如果他们尝试使用 this 语法创建这个类,这真的意味着它是 this 类型。”

这是我发现对你有用的指南:

template <typename T, typename... Args> 
Timer(T&&, std::chrono::milliseconds, Args...) -> Timer<Args...>;

本指南的作用是:他们可以传递任何 T&& 作为第一个参数,它只是被忽略,然后是 chrono 毫秒,然后是 Args 包中的更多类型。鉴于此, Timer 的实际类型(箭头右侧)只是 Timer 无论它们是什么。然后它继续,你的函数只需要转换为 std::function。

现在它匹配您对 lambda 的使用,使用给定的 Args 实例化 Timer,并由此知道它可以将您的 lambda 转换为 std::function。

这是一个工作示例,减去所有不相关的内容:

#include <functional>
#include <chrono>

using namespace std::chrono_literals;

template<typename ...Args>
struct Timer {
  Timer(std::function<void(Args...)> func,  std::chrono::milliseconds step, Args&&... args )
  { }
};

template <typename T, typename... Args>
Timer(T&&, std::chrono::milliseconds, Args...) -> Timer<Args...>;

int main() {
    auto timer = Timer([](int a, int b, float c) { }, 500ms, 15, 17, 12.0f);
}

编译器资源管理器:https://godbolt.org/z/Wa3oK3

【讨论】:

  • 感谢您的详细解释。我会记得发布短代码 sn-p 重点关注确切问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-28
  • 2014-09-22
  • 2011-07-28
  • 1970-01-01
  • 2012-02-07
  • 2022-01-06
相关资源
最近更新 更多