Ошибка вывода аргумента шаблона C ++ 17

Я хотел смоделировать таймер, который будет периодически вызывать функцию (обратный вызов), для этого я написал следующий фрагмент (хотя и наивный), в котором вывод аргумента не выполняется в строке Timer / ‹int, int, float› /. .в основной функции. Я компилирую этот код с помощью 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;
}

person DangoPython    schedule 29.10.2020    source источник
comment
Я думаю, что это не удается, поскольку 1-й аргумент - это лямбда, а не std :: function.   -  person Eugene    schedule 29.10.2020


Ответы (2)


Здесь есть 2 проблемы с выводом аргументов шаблона. Параметр std::function не может быть выведен из лямбда-аргумента, а вывод работает только для пакетов параметров, когда они замыкаются.

Чтобы исправить это, вы можете изменить порядок аргументов конструктора, например:

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...>;

Вот демонстрация.

Обратите внимание на предупреждение, ваш список инициализаторов членов находится не в том же порядке, что и объявления членов.

person cigien    schedule 29.10.2020
comment
Спасибо, я упустил возможность подумать о руководстве по выводам. Также спасибо исправлю порядок в списке инициализаторов - person DangoPython; 29.10.2020

Я удалил все, что не имело отношения к вашему вопросу (вы должны сделать это, чтобы облегчить получение помощи), и вот что мне нужно для работы. Сначала я переместил миллисекунды перед вариативными аргументами. Это может сбить компилятор с толку, где заканчиваются пакеты параметров, поэтому обычно вы хотите, чтобы ... было последним в списке аргументов.

Во-вторых, CTAD (вывод аргументов шаблона класса) не работает для вас из-за необходимости преобразовать вашу лямбду в 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, это на самом деле означает, что это этот тип.

Вот руководство, которое я нашел для вас:

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

В этом руководстве говорится: они могут передавать все, что угодно, T && в качестве первого параметра, и он просто игнорируется, за ним следуют хроно миллисекунды, а затем еще несколько типов в пакете Args. Учитывая это, фактический тип таймера (справа от стрелки) - это просто таймер ‹аргументы ...› какими бы они ни были. Затем он продолжается, и ваша функция просто должна быть преобразована в std :: function.

Теперь он соответствует вашему использованию с лямбда, создает экземпляр Timer с заданными аргументами и, исходя из этого, знает, что он может преобразовать вашу лямбду в функцию std ::.

Вот рабочий пример без всего, что не было связано:

#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

person Chris Uzdavinis    schedule 29.10.2020
comment
Спасибо за подробное объяснение. Я не забуду опубликовать фрагмент короткого кода с упором на конкретную проблему - person DangoPython; 29.10.2020