จะยอมรับตัววนซ้ำทั่วไปได้อย่างไร

ฉันต้องการให้ Constructor ยอมรับตัววนซ้ำใด ๆ ที่มีลักษณะ x และอ้างอิงคลาส y

Class(std::iterator<std::random_access_iterator_tag, MyClass*> it);

แต่เมื่อฉันพยายามส่งตัววนซ้ำ การคอมไพล์ล้มเหลวด้วย Candidate constructor not viable: no known conversion from 'iterator' (aka '__deque_iterator<value_type, pointer, reference, __map_pointer, difference_type, __block_size>') to 'std::iterator<std::random_access_iterator_tag, MyClass *>'

รหัสการแทรก:

std::deque<MyClass*> collection_with_random_access_iterator{};
Class tmp(collection_with_random_access_iterator.begin());

ฉันจะทำอะไรที่นี่?


person Appleshell    schedule 07.09.2013    source แหล่งที่มา
comment
เป็นที่น่าสังเกตว่าคุณได้รับข้อผิดพลาดเนื่องจาก (อย่างน้อยในการใช้งานของคุณ) deque<T>::iterator ไม่ได้รับการสืบทอดจาก iterator<random_access_iterator_tag, T> (มาตรฐานไม่จำเป็นต้องทำเช่นนั้น) (อย่างไรก็ตาม a MyClass** เป็นตัววนซ้ำแบบเข้าถึงแบบสุ่มถึง MyClass* เช่นกัน (และเช่น vector<MyClass*>::iterator ก็เป็นแบบนั้นได้) แต่ตัวชี้แบบดิบไม่สามารถหาได้จากสิ่งใดเลย)   -  person gx_    schedule 07.09.2013
comment
ในขณะที่เราพูดถึงมัน .. std::iterator ไม่ใช่ polymorphic มันเป็นเพียงคลาสฐานตัวช่วยที่มีให้เพื่อลดความซับซ้อนของคำจำกัดความของตัววนซ้ำ มันไม่ควรใช้แบบนั้น   -  person sbabbi    schedule 07.09.2013
comment
@sbabbi True ขอบคุณที่ชี้ให้เห็น การสืบทอดจากโครงสร้างความสะดวก std::iterator แม้ว่าจะเปิดเผยต่อสาธารณะก็เป็นเพียงรายละเอียดการใช้งานเท่านั้น   -  person gx_    schedule 07.09.2013
comment
@gx_ น่าเสียดายที่มาตรฐาน c++ ไม่ต้องการ iterators ของคอลเลกชันมาตรฐานที่จะได้มาจากพาเรนต์ทั่วไป   -  person Appleshell    schedule 07.09.2013
comment
@AdamS Pointers มีคุณสมบัติเป็นตัววนซ้ำเข้าถึงแบบสุ่มและ vector<T>::iterator และ basic_string<T>::iterator สามารถเป็น T * อย่างไรก็ตาม มรดกจะไม่เกี่ยวข้อง คุณไม่ต้องการใช้ฟังก์ชันเสมือนเพื่อยกเลิกการอ้างอิงตัววนซ้ำ   -  person Potatoswatter    schedule 08.09.2013


คำตอบ (2)


โดยทั่วไปตัววนซ้ำจะได้รับการยอมรับตามค่า จากนั้นจึงถูกเลือกปฏิบัติโดยการมอบหมายไปยังฟังก์ชันอื่นๆ

ตัวอย่างเช่น,

    template< typename iterator >
    Class( iterator it ) {
        init_class( * it, typename std::iterator_traits< iterator >::category() );
    }

    template< typename iterator >
    void init_class( iterator it, std::random_access_iterator_tag ) {
        for ( int i = 0; i != 42; i +=3 ) {
            do_something( it[ i ] );
        }
    }

    void do_something( MyClass * ) { … }
};

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

หากไม่จำเป็นต้องเลือกพฤติกรรมที่แตกต่างกัน แต่ต้องการให้แน่ใจว่าผู้ใช้ผ่านตัววนซ้ำการเข้าถึงแบบสุ่มมากกว่า MyClass * ให้ใช้ static_asserts คู่กับเงื่อนไข std::is_same เป็นคำตอบของ sbabbi ประสบการณ์ผู้ใช้ที่ได้นั้นดีกว่า SFINAE เพียงอย่างเดียว เนื่องจากข้อความแสดงข้อผิดพลาดแจ้งว่า "โปรดส่งตัววนซ้ำที่เข้าถึงโดยสุ่ม" ไม่ใช่ "ไม่พบโอเวอร์โหลด"

person Potatoswatter    schedule 07.09.2013
comment
เป็นที่น่าสังเกตว่าการส่งแท็กและ/หรือ static_assert ไม่สามารถเสมอแทนที่ SFINAE ได้ โดยเฉพาะในบางครั้งสำหรับตัวสร้างและการโต้ตอบกับคุณลักษณะเช่น is_constructible ดู flamingdangerzone.com/cxx11/2013 /02/11/ - person gx_; 07.09.2013
comment
@gx_ เครื่องมือที่เหมาะสมสำหรับงาน มีบางอย่างผิดปกติกับคำตอบนี้หรือวันนี้คุณหมดคะแนนโหวตแล้ว? ;วี) - person Potatoswatter; 07.09.2013
comment
ไม่มีอะไรผิด ฉันแค่ไม่คิดจะลงคะแนน แก้ไขแล้ว :) (ไม่ใช่ว่าคุณต้องการตัวแทนเพิ่ม แต่เป็นเรื่องจริงที่การโหวตเป็นสิ่งสำคัญ) - person gx_; 07.09.2013

แล้ว:

template<class Iterator>
Class(Iterator it,
  typename std::enable_if<
          std::is_same<
                  typename std::iterator_traits<Iterator>::value_type,
                  MyClass*
          >::value //checks for value_type
          &&
          std::is_base_of<
                  std::random_access_iterator_tag,
                  typename std::iterator_traits<Iterator>::iterator_category
          >::value //checks for iterator category
    >::type * = 0);

แก้ไข นอกจากนี้ คุณควรพิจารณาแทนที่ std::is_same แรกด้วย std::is_convertible และตรวจสอบ const MyClass* หากคุณจะไม่แก้ไขอินพุต

person sbabbi    schedule 07.09.2013
comment
ขอบคุณ ดูเหมือนเป็นทางเลือก แต่ก็ค่อนข้างใหญ่เช่นกัน ฉันต้องการโซลูชันที่มีขนาดกะทัดรัดกว่านี้ แต่ฉันจะจำไว้และกลับมาใหม่หากไม่มี - person Appleshell; 07.09.2013
comment
@AdamS: ทางเลือกอื่นคือยอมรับประเภทใดก็ได้บางทีอาจเรียกพารามิเตอร์เทมเพลตบางอย่างเช่น RandomIterator เพื่อบันทึกข้อกำหนดที่ไม่เป็นทางการ นั่นทำให้โค้ดง่ายขึ้น แต่คุณจะได้รับข้อความแสดงข้อผิดพลาดที่น่ารังเกียจยิ่งขึ้นหากประเภทไม่ตรงตามข้อกำหนด หรือรอแนวคิดที่จะเพิ่มเข้าไปในภาษา หวังว่าปีหน้า - person Mike Seymour; 07.09.2013
comment
คุณสามารถทำให้ enable_if ใช้รายละเอียดน้อยลงได้: flamingdangerzone.com/cxx11/2012 /06/01/almost-static-if.html (ที่มา: stackoverflow.com/a/14623831 ) - person gx_; 07.09.2013
comment
@Mike Seymour นั่นอาจเป็นคู่ที่ดีกับ static_assert (สมมติว่านี่เป็นตัวสร้างเทมเพลตเพียงตัวเดียวในคลาส) - person sbabbi; 07.09.2013