เมื่อใดที่ฉันควรใช้ string_view ในอินเทอร์เฟซ


16

ฉันใช้ห้องสมุดภายในที่ถูกออกแบบมาเพื่อเลียนแบบเสนอห้องสมุด C ++และในช่วงไม่กี่ปีที่ผ่านมาผมเห็นอินเตอร์เฟซที่เปลี่ยนจากการใช้เพื่อstd::stringstring_view

ดังนั้นฉันจึงเปลี่ยนรหัสตามหน้าที่ให้สอดคล้องกับอินเทอร์เฟซใหม่ น่าเสียดายที่สิ่งที่ฉันต้องผ่านคือพารามิเตอร์ std :: string และสิ่งที่เป็นค่าส่งคืน std :: string ดังนั้นรหัสของฉันเปลี่ยนจากสิ่งนี้:

void one_time_setup(const std::string & p1, int p2) {
   api_class api;
   api.setup (p1, special_number_to_string(p2));
}

ถึง

void one_time_setup(const std::string & p1, int p2) {
   api_class api;
   const std::string p2_storage(special_number_to_string(p2));
   api.setup (string_view(&p1[0], p1.size()), string_view(&p2_storage[0], p2_storage.size()));
}

ฉันจริงๆไม่เห็นสิ่งที่เปลี่ยนแปลงนี้ซื้อฉันเป็นลูกค้าของ API นอกเหนือจากรหัสอื่น ๆ (ที่จะเป็นไปได้กรูขึ้น) การเรียก API นั้นปลอดภัยน้อยกว่า (เนื่องจาก API ไม่ได้เป็นเจ้าของที่เก็บข้อมูลสำหรับพารามิเตอร์อีกต่อไป) อาจบันทึกโปรแกรมของฉันเป็น 0 (เนื่องจากการย้ายคอมไพเลอร์ออปติไมซ์ที่ดีที่สุดสามารถทำได้ตอนนี้) และแม้ว่าจะบันทึกงาน การจัดสรรสองสามอย่างที่จะไม่ทำและจะไม่ทำหลังจากเริ่มทำงานหรือในลูปขนาดใหญ่ที่ใดที่หนึ่ง ไม่ใช่สำหรับ API นี้

อย่างไรก็ตามวิธีนี้ดูเหมือนว่าจะทำตามคำแนะนำที่ฉันเห็นที่อื่นเช่นคำตอบนี้ :

นอกจากนี้เนื่องจาก C ++ 17 คุณควรหลีกเลี่ยงการส่ง const std :: string & ตามด้วย std :: string_view:

ฉันพบว่าคำแนะนำที่น่าแปลกใจเนื่องจากดูเหมือนว่าจะสนับสนุนการแทนที่วัตถุที่ค่อนข้างปลอดภัยด้วยการใช้วัตถุที่ปลอดภัยน้อยลง

ดังนั้นเมื่อใดควรใช้ string_view และควรใช้เมื่อใด


1
คุณไม่ควรจะต้องเรียกstd::string_viewนวกรรมิกโดยตรงคุณควรจะผ่านสายไปยังวิธีการรับstd::string_viewโดยตรงและมันจะแปลงโดยอัตโนมัติ
Mgetz

@Mgetz - อืม ฉันยังไม่ได้ใช้คอมไพเลอร์ C ++ 17 แบบเต็มดังนั้นอาจเป็นปัญหาส่วนใหญ่ ถึงกระนั้นรหัสตัวอย่างที่นี่ดูเหมือนจะบ่งบอกถึงความต้องการอย่างน้อยเมื่อมีการประกาศรหัส
TED

4
ดูคำตอบของฉันผู้ดำเนินการแปลงอยู่ใน<string>ส่วนหัวและเกิดขึ้นโดยอัตโนมัติ รหัสนั้นเป็นการหลอกลวงและผิด
Mgetz

1
"มีความปลอดภัยน้อย" ชิ้นมีความปลอดภัยน้อยกว่าการอ้างอิงสตริงอย่างไร
CodesInChaos

3
@TED ​​ผู้เรียกสามารถปล่อยสตริงที่การอ้างอิงของคุณชี้ไปได้อย่างง่ายดายเช่นเดียวกับที่พวกเขาสามารถเพิ่มหน่วยความจำที่ชิ้นส่วนชี้ไป
CodesInChaos

คำตอบ:


18
  1. ฟังก์ชันการรับค่าจำเป็นต้องเป็นเจ้าของสตริงหรือไม่? ถ้าเป็นเช่นนั้นใช้std::string(ไม่ใช่ const, ไม่ใช่อ้างอิง) ตัวเลือกนี้ให้คุณเลือกที่จะย้ายค่าอย่างชัดเจนเช่นกันหากคุณรู้ว่าจะไม่ถูกใช้อีกในบริบทการโทร
  2. ฟังก์ชั่นเพิ่งอ่านสตริงหรือไม่? ถ้าเป็นเช่นนั้นให้ใช้std::string_view(const, non-ref) นี่เป็นเพราะstring_viewสามารถจัดการได้อย่างง่ายดายstd::stringและchar*ไม่มีปัญหาและไม่ต้องทำสำเนา สิ่งนี้ควรแทนที่const std::string&พารามิเตอร์ทั้งหมด

ในที่สุดคุณไม่ควรต้องเรียกstd::string_viewนวกรรมิกเหมือนคุณ std::stringมีโอเปอเรเตอร์การแปลงที่จัดการการแปลงโดยอัตโนมัติ


เพื่อชี้แจงจุดหนึ่งฉันคิดว่าผู้ดำเนินการแปลงจะดูแลปัญหาที่เลวร้ายที่สุดของอายุการใช้งานด้วยการทำให้ค่าสตริง RHS ของคุณยังคงอยู่ตลอดความยาวของการโทร?
TED

3
@TED ​​หากคุณเพิ่งอ่านค่าจากนั้นค่าจะอยู่นานกว่าการโทร หากคุณเป็นเจ้าของแล้วมันจะต้องอยู่ได้นานกว่าโทร ดังนั้นทำไมฉันจึงพูดถึงทั้งสองกรณี ตัวดำเนินการแปลงนั้นเกี่ยวข้องกับการทำให้std::string_viewใช้งานง่ายขึ้น หากนักพัฒนาซอฟต์แวร์ใช้ผิดในสถานการณ์ที่เป็นเจ้าของนั่นเป็นข้อผิดพลาดในการเขียนโปรแกรม std::string_viewไม่ใช่การเป็นเจ้าของอย่างเคร่งครัด
Mgetz

ทำไมconst, non-ref? พารามิเตอร์ที่เป็น const นั้นขึ้นอยู่กับการใช้งานเฉพาะ แต่โดยทั่วไปมีความสมเหตุสมผลว่าไม่ใช่ const และคุณพลาด3. สามารถรับส่วนแบ่งได้
v.oddou

มีปัญหาอะไรที่ผ่านconst std::string_view &ในสถานที่ของconst std::string &?
ceztko

@ceztko มันไม่จำเป็นอย่างสมบูรณ์และเพิ่มทางอ้อมพิเศษเมื่อเข้าถึงข้อมูล
Mgetz

15

A std::string_viewนำประโยชน์บางอย่างของ a const char*ไปยัง C ++: ซึ่งแตกต่างจากstd::stringstring_view

  • ไม่ได้เป็นเจ้าของหน่วยความจำ
  • ไม่จัดสรรหน่วยความจำ
  • สามารถชี้ไปยังสตริงที่มีอยู่ในออฟเซ็ตและ
  • std::string&มีหนึ่งในระดับน้อยร้ายชี้กว่า

ซึ่งหมายความว่า string_view สามารถหลีกเลี่ยงการคัดลอกได้โดยไม่ต้องจัดการกับพอยน์เตอร์ดิบ

ในรหัสที่ทันสมัยstd::string_viewควรแทนที่การใช้const std::string&พารามิเตอร์ฟังก์ชั่นเกือบทั้งหมด นี้ควรจะมีการเปลี่ยนแปลงแหล่งที่มาได้ตั้งแต่ประกาศผู้ประกอบการที่จะแปลงstd::stringstd::string_view

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


2
ข้อเสียเปรียบครั้งใหญ่std::string_viewคือการไม่มี c_str()วิธีการส่งผลให้std::stringวัตถุระดับกลางที่ไม่จำเป็นซึ่งจำเป็นต้องสร้างและจัดสรร นี่เป็นปัญหาโดยเฉพาะอย่างยิ่งใน API ระดับต่ำ
Matthias

1
@ Matias นั่นเป็นจุดที่ดี แต่ฉันไม่คิดว่ามันจะเป็นข้อเสียอย่างใหญ่หลวง มุมมองสตริงช่วยให้คุณสามารถชี้ไปยังสตริงที่มีอยู่ในบางออฟเซ็ต สตริงย่อยนั้นไม่สามารถเป็นศูนย์ได้คุณต้องคัดลอก มุมมองสตริงไม่ได้ห้ามมิให้คุณทำสำเนา อนุญาตให้ใช้งานการประมวลผลสตริงจำนวนมากที่สามารถดำเนินการกับตัววนซ้ำ แต่คุณคิดถูกว่า API ที่ต้องการสตริง C จะไม่ได้รับประโยชน์จากการดู การอ้างอิงสตริงนั้นจะเหมาะสมกว่า
amon

@ Matias, ไม่ string_view :: data () จับคู่ c_str ()?
Aelian

3
@Jeevaka สตริง C ต้องสิ้นสุดการเป็นศูนย์ แต่ข้อมูลของมุมมองสตริงมักจะไม่สิ้นสุดที่ศูนย์เพราะจะชี้ไปที่สตริงที่มีอยู่ เช่นถ้าเรามีสตริงabcdef\0และมุมมองสตริงที่ชี้ไปที่cdeสตริงย่อยจะไม่มีอักขระเป็นศูนย์หลังจากe- สตริงเดิมfมี มาตรฐานบันทึกเพิ่มเติม:“ข้อมูล () อาจจะกลับมาเป็นตัวชี้การบัฟเฟอร์ที่ไม่สิ้นสุดด้วยค่า null ดังนั้นโดยทั่วไปจะเป็นความผิดพลาดในการส่งผ่านข้อมูล () ไปยังฟังก์ชันที่ใช้เวลาเพียง const charT * และคาดว่าจะเป็นสตริงที่สิ้นสุดด้วยค่า Null”
amon

1
@kayleeFrye_onDeck ข้อมูลอยู่แล้วเป็นตัวชี้อักขระ ปัญหาเกี่ยวกับสาย C ไม่ได้รับตัวชี้ถ่าน แต่ที่สาย C จะต้องสิ้นสุดลงเป็นโมฆะ ดูความคิดเห็นก่อนหน้าของฉันสำหรับตัวอย่าง
amon

8

ฉันพบว่าคำแนะนำที่น่าแปลกใจเนื่องจากดูเหมือนว่าจะสนับสนุนการแทนที่วัตถุที่ค่อนข้างปลอดภัยด้วยการใช้วัตถุที่ปลอดภัยน้อยลง

ฉันคิดว่านี่เป็นจุดประสงค์ที่เข้าใจผิดเล็กน้อย ในขณะที่มันเป็น "การเพิ่มประสิทธิภาพ" จริงๆคุณควรจะคิดว่ามันเป็น unshackling std::stringตัวเองจากที่มีการใช้

ผู้ใช้ C ++ ได้สร้างคลาสสตริงที่แตกต่างกันหลายสิบคลาส คลาสสตริงที่มีความยาวคงที่, คลาสที่ปรับให้เหมาะสมกับ SSO โดยมีขนาดบัฟเฟอร์เป็นพารามิเตอร์เทมเพลต, คลาสสตริงที่เก็บค่าแฮชที่ใช้ในการเปรียบเทียบและอื่น ๆ บางคนใช้สตริงตาม COW หากมีสิ่งหนึ่งที่โปรแกรมเมอร์ C ++ ชื่นชอบก็คือเขียนคลาสของสตริง

และนั่นจะไม่สนใจสตริงที่สร้างและเป็นเจ้าของโดยไลบรารี C เปล่าchar*อาจมีขนาดบางชนิด

ดังนั้นถ้าคุณกำลังเขียนห้องสมุดบางส่วนและคุณใช้ผู้ใช้ในขณะนี้มีการใช้สิ่งที่สตริงพวกเขาใช้และคัดลอกไปconst std::string& std::stringอาจจะหลายสิบครั้ง

หากคุณต้องการเข้าถึงstd::stringอินเทอร์เฟซเฉพาะของสตริงทำไมคุณต้องคัดลอกสตริง นั่นคือขยะ

เหตุผลหลักที่จะไม่ใช้ string_viewเป็นพารามิเตอร์คือ:

  1. หากเป้าหมายสูงสุดของคุณคือการส่งผ่านสตริงไปยังอินเทอร์เฟซที่ใช้สตริงที่สิ้นสุดด้วย NUL (fopenฯลฯ ) std::stringรับประกันว่าจะถูกยกเลิก NUL string_viewไม่ใช่ และมันง่ายมากที่จะซับสตริงมุมมองเพื่อทำให้มันไม่สิ้นสุด NUL sub-stringing a std::stringจะคัดลอกสตริงย่อยออกไปยังช่วงที่ยกเลิก NUL

    ฉันเขียนชนิดลักษณะ string_view ที่สิ้นสุดด้วย NUL พิเศษสำหรับสถานการณ์นี้ คุณสามารถดำเนินการส่วนใหญ่ได้ แต่ไม่ใช่คนที่ทำลายสถานะที่ยกเลิก NUL (ยกตัวอย่างจากตอนท้าย)

  2. ปัญหาตลอดชีวิต ถ้าคุณต้องการจริงๆที่จะคัดลอกหรือมิฉะนั้นมีอาร์เรย์ของตัวละครอายุยืนเรียกฟังก์ชันที่ดีที่สุดที่จะรัฐนี้ขึ้นหน้าโดยการstd::string const std::string &หรือเพียงแค่std::stringเป็นพารามิเตอร์ค่า ด้วยวิธีนี้หากพวกเขามีสตริงดังกล่าวอยู่แล้วคุณสามารถอ้างสิทธิ์ความเป็นเจ้าของได้ทันทีและผู้โทรสามารถย้ายเข้าสู่สตริงได้หากพวกเขาไม่ต้องการเก็บสำเนาไว้รอบตัว


มันเป็นเรื่องจริงเหรอ? คลาสสตริงมาตรฐานเท่านั้นที่ฉันทราบใน C ++ ก่อนหน้านี้คือ std :: string มีบางอย่างที่สนับสนุนการใช้อักขระ * เป็น "สตริง" เพื่อความเข้ากันได้กับ C ย้อนหลัง แต่ฉันแทบไม่จำเป็นต้องใช้สิ่งนั้น แน่นอนว่ามีคลาสบุคคลที่สามที่ผู้ใช้กำหนดเองสำหรับเกือบทุกอย่างที่คุณสามารถจินตนาการได้และสตริงอาจรวมอยู่ในนั้น แต่ฉันแทบจะไม่ต้องใช้เลย
TED

@TED: เพียงเพราะคุณ "แทบไม่เคยต้องใช้สิ่งเหล่านี้" ไม่ได้หมายความว่าคนอื่นไม่ได้ใช้มันเป็นประจำ string_viewเป็นภาษากลางที่สามารถทำงานกับอะไรก็ได้
Nicol Bolas

3
@TED: นั่นคือเหตุผลที่ฉันพูดว่า "C ++ เป็นสภาพแวดล้อมการเขียนโปรแกรม" ซึ่งตรงข้ามกับ "C ++ เป็นภาษา / ไลบรารี"
Nicol Bolas

2
@TED: " ดังนั้นฉันสามารถพูดอย่างเท่าเทียมกัน" C ++ เป็นสภาพแวดล้อมการเขียนโปรแกรมมีหลายพันชั้นเรียนคอนเทนเนอร์ "? " และมันไม่ แต่ฉันสามารถเขียนอัลกอริทึมที่ทำงานกับตัววนซ้ำและคลาสคอนเทนเนอร์ใด ๆ ที่ตามกระบวนทัศน์นั้นจะทำงานกับมัน ในทางตรงกันข้าม "อัลกอริธึม" ที่สามารถเรียงลำดับอักขระที่ต่อเนื่องกันได้ยากกว่ามากในการเขียน ด้วยstring_viewมันง่าย
Nicol Bolas

1
@TED: อาร์เรย์อักขระเป็นกรณีพิเศษมาก พวกมันเป็นเรื่องธรรมดาและตัวอักขระที่อยู่ติดกันต่างกันจะแตกต่างกันไปในวิธีที่พวกเขาจัดการหน่วยความจำไม่ใช่วิธีที่คุณวนซ้ำข้อมูล ดังนั้นการมีชนิดของช่วงภาษาเดียวที่สามารถครอบคลุมทุกกรณีดังกล่าวโดยไม่ต้องใช้แม่แบบเหมาะสม การวางนัยทั่วไปนอกเหนือจากนี้คือจังหวัดของ Range TS และเทมเพลต
Nicol Bolas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.