ตรวจสอบให้แน่ใจว่าเทมเพลต variadic ไม่มีรายการที่ซ้ำกัน

ฉันเดาว่าปัญหาทั้งหมดของฉันได้รับการอธิบายไว้อย่างดีในชื่อเรื่อง ฉันกำลังพยายามสร้างเทมเพลตคลาส variadic (ใน C++11, C++14 หรือ C++1z)

template<typename ...Types> struct MyVariadicTemplate {};

และตรวจสอบให้แน่ใจว่ารายการประเภทในการสร้างอินสแตนซ์ใดๆ ของ MyVariadicTemplate นั้นเป็นแบบฉีด ดังนั้น ถ้าฉันเรียกใช้โค้ดต่อไปนี้:

MyVariadicTemplate<int, double, int> x;

มันจะไม่คอมไพล์ (ฉันยินดีที่จะทำเช่นนั้นโดยใช้ static_assert)

ฉันขอขอบคุณคำแนะนำ


person Jytug    schedule 06.12.2015    source แหล่งที่มา


คำตอบ (3)


สิ่งนี้สามารถเขียนได้โดยใช้เมตาฟังก์ชันสองตัว

ขั้นแรก IsContained ตรวจสอบว่าประเภทปรากฏในรายการประเภทหรือไม่

template <typename T, typename... List>
struct IsContained;

template <typename T, typename Head, typename... Tail>
struct IsContained<T, Head, Tail...>
{
    enum { value = std::is_same<T, Head>::value || IsContained<T, Tail...>::value };
};

template <typename T>
struct IsContained<T>
{
    enum { value = false };
};

ประการที่สอง IsUnique ตรวจสอบว่ารายการประเภทไม่มีรายการซ้ำหรือไม่ ใช้ IsContained เพื่อตรวจสอบการรวมองค์ประกอบทั้งหมด

template <typename... List>
struct IsUnique;

template <typename Head, typename... Tail>
struct IsUnique<Head, Tail...>
{
    enum { value = !IsContained<Head, Tail...>::value && IsUnique<Tail...>::value };
};

template <>
struct IsUnique<>
{
    enum { value = true };
};

ด้วยเครื่องมือเหล่านี้ การยืนยันแบบคงที่จึงง่ายมาก:

template <typename... Ts>
struct NoDuplicates
{
    static_assert(IsUnique<Ts...>::value, "No duplicate types allowed");
};
person TheOperator    schedule 06.12.2015
comment
ว้าวนั่นฉลาดนะ ฉันเพิ่งเห็นตัวอย่างแฟคทอเรียล แต่ฉันไม่คิดจะใช้มัน มันมีประโยชน์ในการทำงานต่อไปของฉันเช่นกัน เพราะยังไงฉันก็จำเป็นต้องมี IsContained ขอบคุณ - person Jytug; 07.12.2015
comment
ฉันรัก enum { value = ... } แทน static constexpr auto value = ... - person Joel Cornett; 07.12.2015

หากคุณมีสิทธิ์เข้าถึงนิพจน์การพับ C++1z คุณสามารถทำให้คำตอบของ @TheOperator ง่ายขึ้นได้โดยทำสิ่งต่อไปนี้สำหรับ IsContained:

template <typename T, typename... List>
struct IsContained;

template <typename T, typename Head, typename... Tail>
struct IsContained<T, Head, Tail...>
{
    enum { value = std::is_same<T, Head>::value || (IsContained<T, Tail>::value && ... && true) };
};

template <typename T>
struct IsContained<T>
{
    enum { value = false };
};

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

person JKor    schedule 06.12.2015
comment
ขอบคุณ. ด้วยความอยากรู้อยากเห็น - จำเป็นต้องเพิ่ม ( && true) ต่อท้ายนิพจน์พับหรือไม่ มีการกล่าวถึง en.cppreference.com/w/cpp/ language/fold ที่นี่ ว่าการพับ && ประเมินเป็นจริงเมื่อได้รับแพ็กว่าง - person Jytug; 07.12.2015
comment
@Jytug ในการประชุมมาตรฐานล่าสุดพวกเขาลบค่าเริ่มต้นทั้งหมดสำหรับการพับแบบเอกนารีและทำการพับแบบเอกนารีโดยมีแพ็คเปล่าเกิดขึ้น (ดู P0160R0) นั่นเป็นเหตุผลที่ฉันทำไบนารีพับ (ด้วย && จริง) - person JKor; 07.12.2015
comment
(IsContained‹T, Tail›::value && ... && true) นี่ไม่ถูกต้อง! ให้ใช้ (IsContained‹T, Tail›::value || ... || false) แทน เนื่องจากผลลัพธ์ของนิพจน์การพับควรเป็นจริงในกรณีที่ IsContained‹T, Tail›::value ใด ๆ เป็นจริง - person sigmaN; 03.05.2020

ด้วย c++17 มันค่อนข้างง่าย

#include <type_traits>

template <typename T, typename... Ts>
struct are_distinct: std::conjunction<
    std::negation<std::is_same<T, Ts>>...,
    are_distinct<Ts...>
>{};

template <typename T>
struct are_distinct<T>: std::true_type{};

อัลกอริทึมเหมือนกับที่แสดงโดย Jkor และ ผู้ดำเนินการ แต่แสดงได้กระชับกว่ามาก

จากนั้น เมื่อมาถึงคำถามเฉพาะของคุณ คุณจะใช้มันในลักษณะนี้:

template<typename ...Types> 
struct MyVariadicTemplate {
    static_assert(are_distinct<Types...>::value, "Types must be distinct");

    /* rest of the code */
};

อย่างไรก็ตาม เนื่องจากนี่คือ O(n²) ฉันสงสัยว่ามีวิธีทำให้อัลกอริทึมซับซ้อนน้อยลงหรือไม่ เอกลักษณ์ของ Boost::mpl ‹› คือ O( n) แต่ฉันไม่สามารถเข้าใจอัลโกที่ใช้ได้

person Fabio A.    schedule 21.03.2021