Pengurangan argumen templat C++17 gagal

Saya ingin mensimulasikan pengatur waktu yang akan memanggil fungsi (panggilan balik) secara berkala untuk itu saya menulis cuplikan berikut (walaupun naif) di mana, Pengurangan argumen gagal pada baris Timer/‹int, int, float›/. .dalam fungsi utama. Saya mengkompilasi kode ini dengan c++17 std. Bagaimana saya bisa memperbaikinya? atau argumen itu diperlukan? Bantuan apa pun dalam hal ini sangat kami hargai.

#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 sumber
comment
Saya pikir ini gagal karena argumen pertama adalah lambda daripada std::function.   -  person Eugene    schedule 29.10.2020


Jawaban (2)


Ada 2 masalah dengan pengurangan argumen templat di sini. Parameter std::function tidak dapat disimpulkan dari argumen lambda, dan pengurangan hanya berfungsi untuk paket parameter saat paket tersebut berada di belakang.

Untuk memperbaikinya, Anda dapat mengubah urutan argumen ke konstruktor, seperti ini:

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

dan tambahkan panduan pengurangan

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

Berikut demo.

Perhatikan peringatannya, daftar penginisialisasi anggota Anda tidak dalam urutan yang sama dengan deklarasi anggota.

person cigien    schedule 29.10.2020
comment
Terima kasih, saya rindu memikirkan panduan deduksi. Juga, terima kasih saya akan memperbaiki urutan daftar penginisialisasi - person DangoPython; 29.10.2020

Saya menghapus semua yang tidak berhubungan dengan pertanyaan Anda (Anda harus melakukan itu untuk membuatnya lebih mudah mendapatkan bantuan), dan inilah yang harus saya kerjakan. Pertama, saya memindahkan milidetik sebelum argumen variadik. Ini dapat membingungkan kompiler di mana paket parameter berakhir, jadi Anda biasanya ingin ... menjadi yang terakhir dalam daftar argumen.

Kedua, CTAD (Pengurangan argumen templat kelas) tidak berfungsi untuk Anda karena kebutuhan untuk mengonversi lambda Anda menjadi fungsi std::sebelum dapat cocok.

Jika main() meneruskan std::function aktual ke konstruktor Anda seperti yang diharapkan, maka ia berfungsi:

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

Tapi itu bisa dibilang terlihat lebih buruk daripada yang Anda coba sebelumnya.

Perbaikan yang lebih baik, yang agak tidak jelas, adalah membantu CTAD mengetahui apa yang Anda lakukan dengan panduan pengurangan c++17. Ini adalah pencocokan pola yang mengatakan, jika mereka mencoba membuat kelas ini menggunakan sintaks ini, itu berarti tipenya ini.

Berikut panduan yang menurut saya cocok untuk Anda:

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

Apa yang dilakukan panduan ini adalah: mereka dapat meneruskan T&& apa pun sebagai parameter pertama dan diabaikan begitu saja, dan itu diikuti oleh krono milidetik, lalu beberapa tipe lainnya dalam paket Args. Mengingat itu, tipe Timer sebenarnya (kanan panah) hanyalah Timer‹Args...› apa pun itu. Kemudian dilanjutkan, dan fungsi Anda hanya perlu diubah menjadi fungsi std::.

Sekarang ini mencocokkan penggunaan Anda dengan lambda, membuat instance Timer dengan Args yang diberikan, dan dari situ ia dapat mengonversi lambda Anda ke fungsi std::.

Berikut ini contoh yang berfungsi, tanpa semua hal yang tidak terkait:

#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);
}

Penjelajah Kompiler: https://godbolt.org/z/Wa3oK3

person Chris Uzdavinis    schedule 29.10.2020
comment
Terima kasih untuk penjelasan rinci. Saya akan ingat untuk memposting cuplikan kode pendek dengan fokus pada masalah sebenarnya - person DangoPython; 29.10.2020