นี่เป็นข้อบกพร่องของคอมไพเลอร์ VC++ 2010 หรือไม่

การใช้ Visual Studio 2010 SP1:

#include <vector>

//namespace XXX {
  struct Test
  {
    bool operator==(const Test& r) const  { return true; }
  };
//}
//typedef XXX::Test Test;

template <typename T> inline bool operator!=(const T& l,const T& r) 
{ return !(l==r); }

int main()
{
  std::vector<Test> vt;
  std::vector<Test> vt2 = std::move(vt);
  return 0;
}

หากฉันคอมไพล์โค้ดด้านบนตามที่เป็นอยู่ มันจะล้มเหลวโดยมีข้อผิดพลาดนี้:

1>C:\apps\MVS10\VC\include\vector(609): error C2593: 'operator !=' is ambiguous
1>          C:\apps\MVS10\VC\include\xmemory(268): could be 'bool std::operator !=<_Ty,_Ty>(const std::allocator<_Ty> &,const std::allocator<_Ty> &) throw()'
1>          with
1>          [
1>              _Ty=Test
1>          ]
1>          test.cpp(11): or       'bool operator !=<std::allocator<_Ty>>(const T &,const T &)' [found using argument-dependent lookup]
1>          with
1>          [
1>              _Ty=Test,
1>              T=std::allocator<Test>
1>          ]
1>          while trying to match the argument list '(std::allocator<_Ty>, std::allocator<_Ty>)'
1>          with
1>          [
1>              _Ty=Test
1>          ]
1>          C:\apps\MVS10\VC\include\vector(606) : while compiling class template member function 'void std::vector<_Ty>::_Assign_rv(std::vector<_Ty> &&)'
1>          with
1>          [
1>              _Ty=Test
1>          ]

... โดยที่ vector(609) แก้ไขเป็นบรรทัดนี้:

        else if (get_allocator() != _Right.get_allocator())

OTOH หากฉันไม่ใส่ข้อคิดเห็นบรรทัดที่เกี่ยวข้องกับ namespace XXX มันจะรวบรวมโดยไม่มีการร้องเรียน

ฉันต้องคิดว่านี่เป็นข้อบกพร่องของคอมไพเลอร์ แต่ฉันกำลังมองหาการตรวจสอบอิสระ

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

อัปเดต: ฉันบันทึกจุดบกพร่องด้วย MS และพวกเขาตอบว่าสิ่งนี้ได้รับการแก้ไขแล้ว "ในคอมไพเลอร์รุ่นถัดไป" ซึ่งฉันคิดว่าหมายถึง Visual C++ 11 ดู: http://connect.microsoft.com/VisualStudio/feedback/details/731692/regression-involving-global-operator-and-std-vector


person mcmcc    schedule 15.03.2012    source แหล่งที่มา
comment
คุณได้ลองสิ่งนี้ด้วยคอมไพเลอร์อื่นแล้วหรือยัง?   -  person Greg Hewgill    schedule 16.03.2012
comment
@GregHewgill: ฉันไม่สามารถทำซ้ำด้วย GCC ได้   -  person mcmcc    schedule 16.03.2012
comment
@GregHewgill: ฉันโกหกในความคิดเห็นก่อนหน้าของฉัน ฉันไม่มีคอมไพเลอร์ตัวอื่นที่รองรับ C ++ 11 ดังนั้นคำตอบคือไม่   -  person mcmcc    schedule 16.03.2012
comment
@LightnessRacesinOrbit: เกี่ยวกับอะไร?   -  person mcmcc    schedule 16.03.2012
comment
@LightnessRacesinOrbit: 'misspoke' แม่นยำยิ่งขึ้น การรวบรวมกรณีทดสอบดั้งเดิมของฉัน (ซึ่งมีจำนวน vt=f()) บน GCC รุ่นเก่าใช้งานได้ แต่เห็นได้ชัดว่านั่นไม่เทียบเท่ากับเวอร์ชันที่รองรับ rvalue-refs อย่างแม่นยำ หลังจากนั้นฉันก็กลั่นมันลงไปที่ std::move()   -  person mcmcc    schedule 16.03.2012
comment
@mcmcc: อ่า! แล้วฉันจะปล่อยคุณไป   -  person Lightness Races in Orbit    schedule 16.03.2012


คำตอบ (1)


มันเป็นข้อผิดพลาด

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

การค้นหาที่ขึ้นอยู่กับอาร์กิวเมนต์ในระหว่างการแก้ไขการเรียก operator!= ระหว่างสอง std::allocator<Test>s ภายในการใช้งานไลบรารีของคุณ [1] อนุญาตให้ค้นหาเนมสเปซของ Test (เช่นเดียวกับ std) เมื่อพยายามค้นหา operator!= ที่จะใช้ [2].

So:

  • ในกรณีที่ใช้งานไม่ได้ เนมสเปซนั้นคือเนมสเปซส่วนกลางซึ่งมี operator!= ที่ตรงกันด้วย ตอนนี้ มันไม่สำคัญแล้ว เพราะฟังก์ชันในเนมสเปซ std เหมาะกว่า [3]; ข้อบกพร่อง VS คือการเพิ่มความคลุมเครือแทน

  • แต่เมื่อ Test อยู่ในเนมสเปซ XXX แทน (แม้จะมี typedef) เนมสเปซที่ค้นหาเนื่องจากกฎข้างต้นจะเป็นเนมสเปซ XXX แทน ซึ่งไม่มีคำจำกัดความที่ขัดแย้งกันสำหรับ operator!=

ทางที่ดีที่สุดอย่ากำหนดโอเปอเรเตอร์สำหรับ ทุกประเภท เช่นนั้น ไม่ว่าในกรณีใดก็ตาม


[1] บางส่วนของการใช้งานสำหรับบรรทัด std::vector<Test> vt2 = std::move(vt); ของคุณบนคอมไพเลอร์/ไลบรารี impl กำลังเรียกใช้ bool operator!=<std::allocator<Test>>(const std::allocator<Test>&, const std::allocator<Test>&)

[2] การอ้างอิงเป็นไปตาม:

[C++11: 3.4.2/1]: เมื่อ postfix-expression ในการเรียกใช้ฟังก์ชัน (5.2.2) เป็น unqualified-id เนมสเปซอื่นๆ จะไม่ได้รับการพิจารณาในระหว่างการค้นหาอย่างไม่มีเงื่อนไขตามปกติ (3.4.1) อาจถูกค้นหา และในเนมสเปซเหล่านั้น การประกาศฟังก์ชันเพื่อนในขอบเขตเนมสเปซ (11.3) อาจมองไม่เห็นเป็นอย่างอื่น การแก้ไขการค้นหาเหล่านี้ขึ้นอยู่กับประเภทของอาร์กิวเมนต์ (และสำหรับอาร์กิวเมนต์เทมเพลตเทมเพลต คือเนมสเปซของอาร์กิวเมนต์เทมเพลต)

[C++11: 3.4.2/2]: สำหรับอาร์กิวเมนต์แต่ละประเภท T ในการเรียกใช้ฟังก์ชัน จะมีชุดของเนมสเปซที่เกี่ยวข้องเป็นศูนย์หรือมากกว่า และชุดของคลาสที่เกี่ยวข้องเป็นศูนย์หรือมากกว่า ที่ต้องพิจารณา . ชุดของเนมสเปซและคลาสถูกกำหนดโดยประเภทของอาร์กิวเมนต์ของฟังก์ชันทั้งหมด (และเนมสเปซของอาร์กิวเมนต์เทมเพลตเทมเพลต) ชื่อ Typedef และ การใช้การประกาศ ที่ใช้เพื่อระบุประเภทไม่สนับสนุนชุดนี้ ชุดของเนมสเปซและคลาสถูกกำหนดในลักษณะต่อไปนี้:

  • [..]
  • ถ้า T เป็นประเภทคลาส (รวมถึงสหภาพ) คลาสที่เกี่ยวข้องจะเป็น: ตัวคลาสเอง; ชั้นเรียนที่เป็นสมาชิกอยู่ ถ้ามี และคลาสพื้นฐานทั้งทางตรงและทางอ้อม เนมสเปซที่เกี่ยวข้องคือเนมสเปซที่คลาสที่เกี่ยวข้องเป็นสมาชิก นอกจากนี้ หาก T เป็นความเชี่ยวชาญพิเศษด้านเทมเพลตคลาส เนมสเปซและคลาสที่เกี่ยวข้องจะรวมถึง: เนมสเปซและคลาสที่เกี่ยวข้องกับประเภทของอาร์กิวเมนต์เทมเพลตที่มีให้สำหรับพารามิเตอร์ประเภทเทมเพลต (ไม่รวมพารามิเตอร์เทมเพลตเทมเพลต); เนมสเปซที่อาร์กิวเมนต์เทมเพลตเทมเพลตใด ๆ เป็นสมาชิก และคลาสที่เทมเพลตสมาชิกใดๆ ที่ใช้เป็นอาร์กิวเมนต์เทมเพลตเทมเพลตเป็นสมาชิก [ หมายเหตุ: อาร์กิวเมนต์เทมเพลตที่ไม่ใช่ประเภทไม่สนับสนุนชุดของเนมสเปซที่เกี่ยวข้อง —หมายเหตุท้าย ]
  • [..]

[3] การอ้างอิงเป็นไปตาม:

[C++11: 13.3.3/1]: จากคำจำกัดความเหล่านี้ ฟังก์ชันที่มีชีวิต F1 ได้รับการกำหนดให้เป็นฟังก์ชันที่ดีกว่าฟังก์ชันที่มีชีวิตอื่นๆ F2 ถ้าสำหรับอาร์กิวเมนต์ทั้งหมด i, ICSi(F1) ไม่ใช่ลำดับการแปลงที่แย่กว่า ICSi(F2) จากนั้น:

  • [..]
  • F1 และ F2 เป็นความเชี่ยวชาญพิเศษของเทมเพลตฟังก์ชัน และเทมเพลตฟังก์ชันสำหรับ F1 นั้นมีความเชี่ยวชาญมากกว่าเทมเพลตสำหรับ F2 ตามกฎการเรียงลำดับบางส่วนที่อธิบายไว้ใน 14.5.6.2

[C++11: 14.5.6.2/2]: การเรียงลำดับบางส่วนจะเลือกว่าเทมเพลตฟังก์ชันใดจากสองเทมเพลตที่มีความเชี่ยวชาญมากกว่าเทมเพลตอื่นโดยการแปลงแต่ละเทมเพลตตามลำดับ (ดูย่อหน้าถัดไป) และดำเนินการหักอาร์กิวเมนต์เทมเพลตโดยใช้ประเภทฟังก์ชัน กระบวนการหักเงินจะกำหนดว่าเทมเพลตใดเทมเพลตหนึ่งมีความเชี่ยวชาญมากกว่าเทมเพลตอื่นหรือไม่ หากเป็นเช่นนั้น เทมเพลตที่พิเศษกว่าคือเทมเพลตที่เลือกโดยกระบวนการสั่งซื้อบางส่วน

การตีความของฉันคือกระบวนการนี้กำหนดว่าฟังก์ชันใน std นั้น "มีความเชี่ยวชาญมากกว่า" มากกว่าฟังก์ชันในเนมสเปซส่วนกลาง ดังนั้นในความเป็นจริงแล้วไม่ควรมีความคลุมเครือ


ขอบคุณ @BoPersson และ @DavidRodríguez สำหรับการมีส่วนร่วมอันมีค่าของคุณในคำตอบสุดเจ๋งนี้

person Lightness Races in Orbit    schedule 15.03.2012
comment
ข้อผิดพลาดแจ้งว่าพบ != เฉพาะสำหรับ Test ทั่วโลกเนื่องจาก ADL สิ่งที่น่าสงสัยคือบรรทัด 606 มีตัวถูกดำเนินการจากเนมสเปซส่วนกลางได้อย่างไร - person Kerrek SB; 16.03.2012
comment
@KerrekSB: ใช่ซึ่งสมเหตุสมผล นั่นเป็นหนึ่งในความขัดแย้งที่กล่าวมาข้างต้น - person Lightness Races in Orbit; 16.03.2012
comment

ตรวจสอบคุณสมบัติ z-index css ของแท็ก Anchor และแท็กส่วนหัว

- person mcmcc; 16.03.2012
comment
@mcmcc: สำหรับฉันดูเหมือนว่า ADL กำลังทำงานอยู่ที่นี่ แม้ว่าข้อบกพร่องของคอมไพเลอร์ใน VC++ จะ ไกล ที่ไม่เคยพบเห็นมาก่อน แต่ฉันไม่คิดว่านี่เป็นข้อผิดพลาดดังกล่าว - person ildjarn; 16.03.2012
comment
ค่าที่เปรียบเทียบเป็นประเภท std::allocator<Test> นั่นจะเป็นการเพิ่มเนมสเปซของ Test ในการค้นหา ADL (นอกเหนือจาก std) - person Bo Persson; 16.03.2012
comment
@BoPersson: ตกลง และเนื่องจาก op!= ที่ขัดแย้งกันนั้นอยู่ในเนมสเปซเดียวกันกับ Test เท่านั้น เมื่อ Test อยู่ในเนมสเปซส่วนกลาง นั่นเป็นครั้งเดียวที่ op!= ที่ขัดแย้งกันถูกฉีดเข้าไปในชุดผู้สมัคร ..? - person Lightness Races in Orbit; 16.03.2012
comment
@KerrekSB: บรรทัด 606 มีตัวถูกดำเนินการจากเนมสเปซส่วนกลางอย่างไร มีคนบอกฉันครั้งหนึ่งว่า ADL นี่มันไอ้เลว... เหตุผลที่ดึงเนมสเปซส่วนกลางสำหรับ ADL ก็คือ Test อยู่ที่นั่น โอ้ แต่องค์ประกอบที่จะเปรียบเทียบไม่ใช่ Test! แต่องค์ประกอบเหล่านี้เป็นความเชี่ยวชาญพิเศษของเทมเพลตที่มี Test และ ADL จะนำเนมสเปซของอาร์กิวเมนต์เทมเพลตที่เกี่ยวข้องมาไว้ในขอบเขตด้วย ADL คือ ไอ้สารเลว :) - person David Rodríguez - dribeas; 16.03.2012
comment
@ DavidRodríguez-dribeas: การอ้างอิงถึงข้อเท็จจริงนั้นจะ อย่างมาก ปรับปรุงคำตอบนี้: D - person Lightness Races in Orbit; 16.03.2012
comment
@LightnessRacesinOrbit: ADL จะนำเนมสเปซของอาร์กิวเมนต์มาไว้ในขอบเขตของฟังก์ชัน และเนมสเปซของอาร์กิวเมนต์เทมเพลตของความเชี่ยวชาญเฉพาะทางในกรณีของเทมเพลต ซึ่งเป็นเหตุผลว่าทำไมถ้า Test อยู่ในเนมสเปซส่วนกลางจะถูกเลือก คำถามอื่น ๆ ซึ่งอาจบ่งบอกถึงปัญหาในการใช้งานคือเหตุใด (มีเทมเพลตด้วย) op!= สำหรับผู้จัดสรรจึงไม่ตรงกันดีกว่าคำถามทั่วไปที่ผู้ใช้ให้มา - person David Rodríguez - dribeas; 16.03.2012
comment
@LightnessRacesinOrbit: 3.4.2/2 [...] นอกจากนี้ หาก T เป็นความเชี่ยวชาญเทมเพลตคลาส เนมสเปซและคลาสที่เกี่ยวข้องจะรวมถึง: เนมสเปซและคลาสที่เกี่ยวข้องกับประเภทของอาร์กิวเมนต์เทมเพลตที่มีให้สำหรับประเภทเทมเพลต พารามิเตอร์ (ไม่รวมพารามิเตอร์เทมเพลตเทมเพลต) - person David Rodríguez - dribeas; 16.03.2012
comment
น่าแปลกที่ สามารถคอมไพล์ได้ดีบน GCC นั่น คอมไพเลอร์บั๊กหรือเปล่า? - person Kerrek SB; 16.03.2012
comment
@ DavidRodríguez-dribeas: ใช่ ฉันคิดว่าคำถามต่อไปคือเหตุใดการล่มสลายทั้งหมดนี้ส่งผลให้เกิดความคลุมเครือแทนที่จะเลือกการแข่งขันรายการใดรายการหนึ่ง - person Lightness Races in Orbit; 16.03.2012
comment
@KerrekSB: ฉันคิดว่า GCC ถูกและ VS ผิด เมื่อพิจารณาว่าเทมเพลตทั้งสองพร้อมใช้งานในบริบทเดียวกัน (หรือถูก ADL นำมา) เทมเพลตที่ผู้ใช้กำหนดจึงตรงกันที่แย่กว่า template <typename T> operator!=( allocator<T> const&, allocator<T> const& ) - person David Rodríguez - dribeas; 16.03.2012
comment
@KerrekSB: อาจเป็นผลมาจากการใช้งานห้องสมุดที่แตกต่างกันเล็กน้อย - person Lightness Races in Orbit; 16.03.2012
comment
@ DavidRodríguez-dribeas: ใช่ ฟังดูดีเลย ข้อกำหนดของการจัดสรรกำหนดอย่างชัดเจนให้คุณจัดเตรียม operator!= ฟรี - person Kerrek SB; 16.03.2012
comment
@ DavidRodríguez-dribeas: แต่เป็นเทมเพลตที่ผู้ใช้กำหนดทั้งคู่ หรือมีการยกเว้นจากการกำหนดโดยผู้ใช้สำหรับฟังก์ชันไลบรารีที่ฉันไม่ทราบ - person Lightness Races in Orbit; 16.03.2012
comment
@LightnessRacesinOrbit: ไม่ ไม่ควร ข้อความแสดงข้อผิดพลาดแจ้งว่าไม่สามารถตัดสินใจระหว่างสองตัวเลือกได้ และฉันเชื่อว่าการใช้ลำดับบางส่วนที่กำหนดไว้ในมาตรฐานที่ผู้ใช้กำหนดนั้นมีความชำนาญ น้อยกว่า มากกว่าลำดับในไลบรารี และด้วยเหตุนี้ คอมไพเลอร์ควรเลือกอันนั้นขึ้นมา ฉันเชื่อมั่นมากขึ้นเรื่อยๆ ว่ามีการนำไปใช้ แต่ไม่ได้อยู่ที่ว่าทำไมจึงพิจารณาเทมเพลตนั้น แต่อยู่ที่ว่าทำไมจึงพิจารณาว่า ดีพอ ๆ กับ อีกแบบหนึ่ง - person David Rodríguez - dribeas; 16.03.2012
comment
@LightnessRacesinOrbit: ในความคิดเห็นของฉันอ่าน ผู้ใช้กำหนด ตามที่ กำหนดโดยผู้ใช้ที่เขียนคำถาม ทั้งสองประเภทเป็นประเภทที่ผู้ใช้กำหนด แต่ฉันไม่ต้องการพูดว่า: ประเภทที่กำหนดโดยบุคคลที่เขียนคำถาม ที่นี่และที่นั่น - person David Rodríguez - dribeas; 16.03.2012
comment
@ DavidRodríguez-dribeas: ฉันเข้าใจว่าตอนนี้เรากำลังพิจารณาว่าข้อผิดพลาด Best Viable Match ที่เป็นไปได้กำลังเกิดขึ้นที่นี่ แต่ฉันยังไม่มั่นใจว่าฉันเห็นมัน - person Lightness Races in Orbit; 16.03.2012
comment
@ DavidRodríguez-dribeas: ตกลง เมื่อดูพารามิเตอร์ที่แตกต่างกันของแต่ละ op!= ในชุดผู้สมัคร ฉันเห็นว่าคุณมาจากไหน อย่างไรก็ตาม ฉันปฏิเสธที่จะพยายามถอดรหัส 13.3.3 ช่วยแก้ให้เราหน่อยแล้วโพสผลเป็นคอมเม้นท์แล้วผมจะขโมยไปครับ ;) - person Lightness Races in Orbit; 16.03.2012
comment
@DavidRodríguez-dribeas: โอเค ฉันโกหก ฉันมองผ่านอย่างรวดเร็ว ฉันไม่สามารถทำสิ่งนี้ไปไกลกว่านี้ได้ เพราะฉันติดอยู่ที่ 14.5.6.2/3 และความเชี่ยวชาญเฉพาะทางนั้น จริงๆ ถูกกำหนดไว้ไม่มากก็น้อย - person Lightness Races in Orbit; 16.03.2012
comment
@LightnessRacesinOrbit บรรทัดการให้เหตุผลง่ายๆ สำหรับผู้เชี่ยวชาญมาก/น้อยคือ: A มีความเชี่ยวชาญมากกว่า B หากการรวมกันของอาร์กิวเมนต์ที่ถูกต้องทั้งหมดของ A นั้นเป็นอาร์กิวเมนต์ที่ถูกต้องสำหรับ B และมีอาร์กิวเมนต์รวมกันอย่างน้อยหนึ่งรายการที่ถูกต้องกับ B แต่ ไม่ใช่กับ A ...foo(T*) จะมีความเชี่ยวชาญมากกว่า ...foo(T) เนื่องจากทุกประเภทที่ตรงกับเทมเพลตแรกก็ตรงกับเทมเพลตที่สองด้วย และมีประเภทที่ตรงกับเทมเพลตที่สอง (ไม่ใช่พอยน์เตอร์) ที่ไม่ถูกต้องในเทมเพลตแรก - person David Rodríguez - dribeas; 16.03.2012
comment
ในกรณีนี้ ตัวจัดสรร op!= สามารถรับได้เฉพาะการสร้างอินสแตนซ์ของ allocator<> ที่เป็นอาร์กิวเมนต์ที่ถูกต้องสำหรับ global op!= แต่มีประเภทต่างๆ (โดยพื้นฐานแล้วอย่างอื่น) ที่เป็นอาร์กิวเมนต์ที่ถูกต้องสำหรับ global และไม่ใช่สำหรับอาร์กิวเมนต์ในไลบรารี - person David Rodríguez - dribeas; 16.03.2012
comment
@ DavidRodríguez-dribeas: ฟังดูสมเหตุสมผล ฉันยังคงไม่สามารถหาการอ้างอิงสำหรับเรื่องนั้นได้ ฉันถึงขีดจำกัดของมาตรฐานฟูของฉันแล้ว คุณอาจต้องการโพสต์คำตอบของคุณเอง แต่ถ้าคุณไม่ทำ ฉันยินดีเป็นอย่างยิ่งที่คุณเขียนข้อความอ้างอิงเพิ่มเติมลงในเชิงอรรถ [3] หากคุณเข้าใจ =) - person Lightness Races in Orbit; 16.03.2012
comment
โปรดตรวจสอบให้แน่ใจว่าส่วนที่เกี่ยวข้องของการสนทนาเพิ่มเติมนี้ได้รับการแก้ไขเป็นคำถามและ/หรือคำตอบ - person Lasse V. Karlsen; 16.03.2012
comment
@ LasseV.Karlsen: IMO ที่ทำเพื่อความพึงพอใจของฉันจนถึงตอนนี้ ฉันแน่ใจว่าเราจะล้างความคิดเห็นเมื่อเสร็จสิ้นสมบูรณ์ - person Lightness Races in Orbit; 16.03.2012
comment
ขอชื่นชมทุกคนที่สนับสนุนคำตอบนี้ เชิงวิชาการมาก. - person mcmcc; 16.03.2012