การหักอาร์กิวเมนต์เทมเพลต 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 จากอาร์กิวเมนต์ 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...>;

นี่คือการสาธิต

โปรดสังเกตคำเตือน รายการตัวเริ่มต้นสมาชิกของคุณไม่อยู่ในลำดับเดียวกับการประกาศสมาชิก

person cigien    schedule 29.10.2020
comment
ขอบคุณ ฉันพลาดที่จะคิดเกี่ยวกับคู่มือการหักเงิน นอกจากนี้ขอขอบคุณฉันจะแก้ไขลำดับรายการเริ่มต้น - person DangoPython; 29.10.2020

ฉันแยกทุกอย่างที่ไม่เกี่ยวข้องกับคำถามของคุณออก (คุณควรทำอย่างนั้นเพื่อให้ขอความช่วยเหลือได้ง่ายขึ้น) และนี่คือสิ่งที่ฉันต้องทำ อันดับแรก ฉันย้ายมิลลิวินาทีก่อนอาร์กิวเมนต์ที่แปรผัน มันอาจทำให้คอมไพลเลอร์สับสนเมื่อแพ็กพารามิเตอร์สิ้นสุด ดังนั้นคุณมักจะต้องการให้ ... เป็นสิ่งสุดท้ายในรายการอาร์กิวเมนต์

ประการที่สอง CTAD (การหัก arg เทมเพลตคลาส) ไม่ทำงานสำหรับคุณเนื่องจากจำเป็นต้องแปลงแลมบ์ดาของคุณเป็น std::function ก่อนจึงจะสามารถจับคู่ได้

หาก main() ส่งผ่าน std::function จริงไปยัง Constructor ของคุณตามที่คาดไว้ มันจะใช้งานได้:

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

แต่นั่นอาจดูแย่กว่าสิ่งที่คุณลองในตอนแรก

การแก้ไขที่ดีกว่าซึ่งค่อนข้างคลุมเครือคือการช่วยให้ CTAD ทราบว่าคุณกำลังทำอะไรกับ คำแนะนำการหักเงิน c++17 นี่เป็นการจับคู่รูปแบบที่บอกว่า หากพวกเขาพยายามสร้างคลาสนี้โดยใช้ไวยากรณ์ นี้ ก็หมายความว่าคลาสนั้นเป็นประเภท นี้ จริงๆ

คำแนะนำที่ฉันพบว่าใช้ได้ผลสำหรับคุณมีดังนี้

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

สิ่งที่คู่มือนี้ทำคือบอกว่า พวกเขาสามารถส่ง T&& อะไรก็ได้เป็นพารามิเตอร์แรก และมันจะถูกละเว้น และตามด้วยโครโนมิลลิวินาที จากนั้นก็มีประเภทอื่นๆ ในแพ็ค Args เนื่องจากประเภทเฉียบพลันของ Timer (ทางขวาของลูกศร) จึงเป็นเพียง Timer‹Args...> ไม่ว่าจะเป็นอะไรก็ตาม จากนั้นจะดำเนินการต่อไป และฟังก์ชันของคุณจะต้องแปลงเป็น std::function ได้

ตอนนี้มันตรงกับการใช้งานของคุณกับแลมบ์ดา สร้างอินสแตนซ์ Timer ด้วย Args ที่กำหนด และจากนั้นก็รู้ว่ามันสามารถแปลงแลมบ์ดาของคุณเป็น 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

person Chris Uzdavinis    schedule 29.10.2020
comment
ขอบคุณสำหรับคำอธิบายโดยละเอียด ฉันจะจำไว้ว่าต้องโพสต์ข้อมูลโค้ดขนาดสั้นโดยเน้นไปที่ปัญหาที่แน่นอน - person DangoPython; 29.10.2020