วนซ้ำกว่า std :: vector: ตัวแปรดัชนีที่ไม่ได้ลงชื่อ vs


470

วิธีที่ถูกต้องในการวนซ้ำเวกเตอร์ใน C ++ คืออะไร?

ลองพิจารณาชิ้นส่วนรหัสสองอันนี้อันนี้ใช้ได้ดี:

for (unsigned i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

และอันนี้:

for (int i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

warning: comparison between signed and unsigned integer expressionsซึ่งจะสร้าง

ฉันใหม่ในโลกของ C ++ ดังนั้นunsignedตัวแปรที่ดูน่ากลัวสำหรับฉันและฉันรู้ว่าunsignedตัวแปรอาจเป็นอันตรายหากไม่ได้ใช้อย่างถูกต้องดังนั้น - ถูกต้องหรือไม่


10
อันที่ไม่ได้ลงนามนั้นถูกต้องเพราะ polygon.size () เป็นประเภทที่ไม่ได้ลงนาม หมายถึงไม่ได้ลงนามในเชิงบวกเสมอหรือ 0 นั่นคือทั้งหมดที่มันหมายถึง ดังนั้นหากการใช้งานของตัวแปรนั้นมีไว้สำหรับการนับเท่านั้นการเลือกที่ไม่ได้ลงนามเป็นตัวเลือกที่ถูก
Adam Bruss

3
@AdamBruss .size()ไม่ได้ชนิดที่รู้จักunsigned มันเป็นประเภทunsigned int std::size_t
underscore_d

1
@underscore_d size_t เป็นนามแฝงสำหรับไม่ได้ลงชื่อ
Adam Bruss

2
@ AdamBruss No. std::size_tเป็น typedef ที่กำหนดโดย _implementation ดูมาตรฐาน std::size_tอาจเทียบเท่ากับunsignedการใช้งานปัจจุบันของคุณ แต่นั่นไม่เกี่ยวข้อง การแกล้งมันอาจส่งผลให้โค้ดไม่สามารถพกพาได้และพฤติกรรมที่ไม่ได้กำหนด
underscore_d

2
@LF ... แน่นอนซึ่งอาจเป็นไปได้std::size_tในทางปฏิบัติ คุณคิดว่าเราได้ครอบคลุมทุกอย่างในกระแสความคิดเห็นนี้นานกว่า 6 ปีหรือไม่?
underscore_d

คำตอบ:


817

สำหรับการทำซ้ำย้อนหลังดูคำตอบนี้

การวนซ้ำไปข้างหน้านั้นเกือบจะเหมือนกัน เพียงแค่เปลี่ยนตัววนซ้ำ / ลดการสลับโดยการเพิ่ม คุณควรเลือกตัววนซ้ำ บางคนบอกให้คุณใช้std::size_tเป็นประเภทตัวแปรดัชนี อย่างไรก็ตามนั่นไม่ใช่พกพา ใช้size_typetypedef ของคอนเทนเนอร์เสมอ (ในขณะที่คุณสามารถหลีกเลี่ยงได้ด้วยการแปลงในกรณี iterating ไปข้างหน้าเท่านั้นมันอาจผิดไปตลอดทางในกรณี iterating ย้อนหลังเมื่อใช้std::size_tในกรณีที่std::size_tกว้างกว่าที่ typedef ของsize_type) :


ใช้ std :: vector

การใช้ตัววนซ้ำ

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    /* std::cout << *it; ... */
}

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

ใช้ช่วง C ++ 11

for(auto const& value: a) {
     /* std::cout << value; ... */

การใช้ดัชนี

for(std::vector<int>::size_type i = 0; i != v.size(); i++) {
    /* std::cout << v[i]; ... */
}

การใช้อาร์เรย์

การใช้ตัววนซ้ำ

for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) {
    /* std::cout << *it; ... */
}

ใช้ช่วง C ++ 11

for(auto const& value: a) {
     /* std::cout << value; ... */

การใช้ดัชนี

for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

อ่านย้อนหลังซ้ำตอบคำตอบว่าปัญหาอะไรที่sizeofแนวทางสามารถให้ได้


ขนาดประเภทของพอยน์เตอร์: การใช้ Dif_type อาจพกพาได้มากกว่า ลอง iterator_traits <element_type *> :: different_type นี้เป็นหนึ่งในคำหนึ่งของการประกาศ แต่มันเป็นแบบพกพาอื่น ๆ ...
wilhelmtell

wilhelmtell สำหรับสิ่งที่ฉันควรใช้ Dif_type sizeof ถูกกำหนดให้ส่งคืน size_t :) ฉันไม่เข้าใจคุณ ถ้าฉันลบพอยน์เตอร์จากกัน, different_type จะเป็นตัวเลือกที่ถูกต้อง
Johannes Schaub - litb

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

1
@ ไม่มีฉันยอมรับว่าการใช้ตัวนับลูปที่ไม่ได้ลงชื่อเป็นความคิดที่ไม่ดี แต่เนื่องจากไลบรารีมาตรฐานใช้ชนิดจำนวนเต็มที่ไม่ได้ลงนามสำหรับดัชนีและขนาดฉันต้องการประเภทดัชนีที่ไม่ได้ลงชื่อสำหรับไลบรารีมาตรฐาน ไลบรารีอื่นจึงใช้เฉพาะชนิดที่ลงชื่อเท่านั้นเช่น Qt lib
Johannes Schaub - litb

32
อัปเดตสำหรับ C ++ 11: ช่วงที่ใช้สำหรับลูป for (auto p : polygon){sum += p;}
Siyuan Ren

170

สี่ปีผ่านไปGoogleได้ให้คำตอบกับฉัน ด้วยมาตรฐานC ++ 11 (หรือที่รู้จักว่าC ++ 0x ) มีวิธีการใหม่ที่น่าพึงพอใจในการทำสิ่งนี้ (ในราคาที่ทำลายความเข้ากันได้แบบย้อนหลัง): autoคำหลักใหม่ มันช่วยให้คุณไม่ต้องยุ่งยากในการระบุชนิดของตัววนซ้ำที่จะใช้ (ทำซ้ำประเภทเวกเตอร์อีกครั้ง) เมื่อเห็นได้ชัด (ไปยังคอมไพเลอร์) ซึ่งเป็นประเภทที่จะใช้ ด้วยvการเป็นคุณvectorคุณสามารถทำสิ่งนี้:

for ( auto i = v.begin(); i != v.end(); i++ ) {
    std::cout << *i << std::endl;
}

C ++ 11ยิ่งไปกว่านั้นและให้ไวยากรณ์พิเศษสำหรับการวนซ้ำคอลเลกชันเช่นเวกเตอร์ มันเอาความจำเป็นในการเขียนสิ่งที่เหมือนกันเสมอ:

for ( auto &i : v ) {
    std::cout << i << std::endl;
}

หากต้องการดูในโปรแกรมทำงานให้สร้างไฟล์auto.cpp:

#include <vector>
#include <iostream>

int main(void) {
    std::vector<int> v = std::vector<int>();
    v.push_back(17);
    v.push_back(12);
    v.push_back(23);
    v.push_back(42);
    for ( auto &i : v ) {
        std::cout << i << std::endl;
    }
    return 0;
}

ในการเขียนสิ่งนี้เมื่อคุณคอมไพล์ไฟล์นี้ด้วยg ++คุณจะต้องตั้งค่าให้ทำงานกับมาตรฐานใหม่ด้วยการให้ค่าสถานะพิเศษ:

g++ -std=c++0x -o auto auto.cpp

ตอนนี้คุณสามารถรันตัวอย่าง:

$ ./auto
17
12
23
42

โปรดทราบว่าคำแนะนำในการรวบรวมและการใช้งานเฉพาะกับคอมไพเลอร์gnu c ++บนLinuxโปรแกรมควรเป็นอิสระจากแพลตฟอร์ม (และคอมไพเลอร์)


7
C ++ 11 มอบให้คุณfor (auto& val: vec)
Flexo

@flexo ขอบคุณฉันไม่รู้ว่าฉันจะลืมได้อย่างไร ฉันเดาว่า C ++ ไม่เพียงพอ ไม่สามารถเชื่อได้ว่ามีบางสิ่งที่ใช้งานได้จริง (คิดว่าเป็นไวยากรณ์ JavaScript จริง ๆ ) ฉันเปลี่ยนคำตอบเพื่อรวมสิ่งนั้น
kratenko

คำตอบของคุณดีมาก ไม่เป็นที่พอใจว่า g ++ รุ่นเริ่มต้นใน devkits ระบบปฏิบัติการต่าง ๆ มีค่าต่ำกว่า 4.3 ซึ่งทำให้ใช้งานไม่ได้
Ratata Tata

คุณต้องการเริ่มต้นเวกเตอร์ด้วยstd::vector<int> v = std::vector<int>();หรือคุณจะใช้std::vector<int> v;แทนได้ง่าย ๆ?
Bill Cheatham

@BillCheatham ดี - ฉันเพิ่งลองใช้โดยไม่ได้กำหนดค่าเริ่มต้นและทำงานได้ดังนั้นจึงดูเหมือนว่าใช้งานไม่ได้
kratenko

44

ในกรณีเฉพาะในตัวอย่างของคุณฉันจะใช้อัลกอริทึม STL เพื่อทำสิ่งนี้ให้สำเร็จ

#include <numeric> 

sum = std::accumulate( polygon.begin(), polygon.end(), 0 );

สำหรับกรณีทั่วไป แต่ก็ยังค่อนข้างเรียบง่ายฉันจะไปกับ:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );

38

เกี่ยวกับคำตอบของ Johannes Schaub:

for(std::vector<T*>::iterator it = v.begin(); it != v.end(); ++it) { 
...
}

นั่นอาจใช้ได้กับคอมไพเลอร์บางตัว แต่ไม่สามารถใช้กับ gcc ได้ ปัญหานี่คือคำถามถ้า std :: vector :: iterator เป็นชนิดตัวแปร (สมาชิก) หรือฟังก์ชัน (เมธอด) เราได้รับข้อผิดพลาดต่อไปนี้ด้วย gcc:

In member function void MyClass<T>::myMethod()’:
error: expected `;' before ‘it’
error: ‘it’ was not declared in this scope
In member function ‘void MyClass<T>::sort() [with T = MyClass]’:
instantiated from ‘void MyClass<T>::run() [with T = MyClass]’
instantiated from here
dependent-name ‘std::vector<T*,std::allocator<T*> >::iterator’ is parsed as a non-type, but instantiation yields a type
note: say ‘typename std::vector<T*,std::allocator<T*> >::iterator’ if a type is meant

การแก้ปัญหาคือการใช้คำหลัก 'typename' ตามที่บอก:

typename std::vector<T*>::iterator it = v.begin();
for( ; it != v.end(); ++it) {
...

2
คุณควรทำอย่างละเอียดว่าสิ่งนี้ใช้เฉพาะเมื่อTเป็นอาร์กิวเมนต์แม่แบบและการแสดงออกstd::vector<T*>::iteratorจึงเป็นชื่อที่ต้องพึ่งพา เพื่อให้ชื่อที่แยกได้นั้นถูกวิเคราะห์คำเป็นประเภทจะต้องมีการเติมtypenameคำสำคัญในคำหลักเนื่องจากการวินิจฉัยบ่งชี้
Reinstate Monica

17

การเรียกเพื่อvector<T>::size()ส่งคืนค่าชนิดstd::vector<T>::size_typeไม่ใช่ int ไม่ได้ลงนาม int หรืออย่างอื่น

โดยทั่วไปแล้วการวนซ้ำในคอนเทนเนอร์ใน C ++ นั้นทำได้โดยใช้ตัววนซ้ำเช่นนี้

std::vector<T>::iterator i = polygon.begin();
std::vector<T>::iterator end = polygon.end();

for(; i != end; i++){
    sum += *i;
}

โดยที่ T คือประเภทของข้อมูลที่คุณจัดเก็บในเวกเตอร์

หรือการใช้ขั้นตอนวิธีการทำซ้ำที่แตกต่างกัน ( std::transform, std::copy, std::fill, std::for_eachฯลฯ )


โดยทั่วไปแล้ว Iterators เป็นความคิดที่ดี แต่ฉันสงสัยว่ามีความจำเป็นต้องเก็บ "สิ้นสุด" ในตัวแปรแยกต่างหากและสามารถทำได้ภายในคำสั่ง for (;;)
Saulius Žemaitaitis

1
ฉันรู้ว่าการเริ่มต้น () และสิ้นสุด () จะถูกตัดจำหน่ายเวลาคงที่ แต่โดยทั่วไปฉันพบว่านี่จะสามารถอ่านได้มากกว่าการยัดทุกอย่างลงในบรรทัดเดียว
Jasper Bekkers

3
คุณสามารถแบ่งเป็นบรรทัดแยกเพื่อปรับปรุงความสามารถในการอ่าน การประกาศตัววนซ้ำนอกลูปหมายความว่าคุณต้องการชื่อตัววนซ้ำที่แตกต่างกันสำหรับทุกลูปบนคอนเทนเนอร์ที่มีประเภทต่างกัน
464142 Jay Conrod

ฉันตระหนักถึงความแตกต่างทั้งหมดและสิ่งที่เป็นพื้นฐานคือความชอบส่วนตัว โดยทั่วไปแล้วฉันจะทำสิ่งต่าง ๆ ได้อย่างไร
Jasper Bekkers

2
@ pihentagy ฉันเดาว่าจะตั้งไว้ในส่วนแรกของ for-loop เช่น. สำหรับ (auto i = polygon.begin (), end = polygon.end (); i! = end; i ++)
Jasper Bekkers

11

การใช้size_t:

for (size_t i=0; i < polygon.size(); i++)

การอ้างถึงWikipedia :

ไฟล์ส่วนหัว stdlib.h และ stddef.h กำหนดประเภทข้อมูลที่เรียกว่าsize_tซึ่งใช้ในการแสดงขนาดของวัตถุ ฟังก์ชั่นที่ใช้ห้องสมุดขนาดคาดหวังให้เป็นชนิดและประเมินผู้ประกอบการที่จะsize_t sizeofsize_t

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


size_t ตกลงสำหรับเวกเตอร์เพราะมันจะต้องเก็บวัตถุทั้งหมดในอาร์เรย์ (ตัวเองวัตถุด้วย) แต่รายการ std :: อาจมีมากกว่า size_t องค์ประกอบ!
MSalters

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

AFAIK ก็แนะนำให้#include <cstddef>มากกว่า<stddef.h>หรือแย่ลงความสมบูรณ์ของ[c]stdlibและใช้std::size_tมากกว่ารุ่นไม่มีเงื่อนไข - และเหมือนกันสำหรับสถานการณ์อื่น ๆ ที่คุณต้องเลือกระหว่างการและ<cheader> <header.h>
underscore_d

7

ประวัติเล็กน้อย:

เพื่อแสดงว่าจำนวนเป็นลบหรือไม่ใช้คอมพิวเตอร์ใช้บิต 'เครื่องหมาย' intเป็นประเภทข้อมูลที่ลงนามแล้วซึ่งหมายความว่าสามารถเก็บค่าบวกและลบ (ประมาณ -2 พันล้านถึง 2 พันล้าน) Unsignedสามารถจัดเก็บตัวเลขที่เป็นบวกได้เท่านั้น (และเนื่องจากมันไม่ได้ทำให้เมตาดาต้าเสียเปล่าเลยมันสามารถเก็บได้มากกว่า: 0 ถึงประมาณ 4 พันล้าน)

std::vector::size()ส่งคืน an unsignedแล้วเวกเตอร์มีความยาวติดลบได้อย่างไร

คำเตือนกำลังบอกคุณว่าตัวถูกดำเนินการด้านขวาของคำสั่งอสมการของคุณสามารถเก็บข้อมูลได้มากขึ้นแล้วทางซ้าย

โดยพื้นฐานแล้วถ้าคุณมีเวกเตอร์ที่มีรายการมากกว่า 2 พันล้านรายการและคุณใช้จำนวนเต็มเพื่อทำดัชนีคุณจะพบปัญหาล้น (int จะย้อนกลับไปเป็นลบ 2 พันล้านรายการ)


6

ฉันมักจะใช้ BOOST_FOREACH:

#include <boost/foreach.hpp>

BOOST_FOREACH( vector_type::value_type& value, v ) {
    // do something with 'value'
}

ใช้งานได้กับคอนเทนเนอร์ STL, อาร์เรย์, สตริงแบบ C, ฯลฯ


2
คำตอบที่ดีบางคำถามอื่น ๆ (ว่าฉันควรย้ำเวกเตอร์?) แต่ไม่สมบูรณ์ได้ทุกสิ่งที่ OP ถูกถาม (สิ่งที่เป็นความหมายของคำเตือนเกี่ยวกับตัวแปรที่ไม่ได้ลงชื่อหรือไม่)
abelenky

3
เขาถามว่าวิธีที่ถูกต้องในการวนซ้ำเวกเตอร์คืออะไร ดังนั้นดูเหมือนว่าเกี่ยวข้องพอ คำเตือนคือสาเหตุที่เขาไม่พอใจกับวิธีแก้ปัญหาปัจจุบันของเขา
jalf

5

จะแล้วเสร็จไวยากรณ์ C ++ 11 เปิดใช้งานอีกหนึ่งรุ่นสำหรับตัววนซ้ำ ( อ้างอิง ):

for(auto it=std::begin(polygon); it!=std::end(polygon); ++it) {
  // do something with *it
}

ซึ่งยังสะดวกสบายสำหรับการทำซ้ำย้อนกลับ

for(auto it=std::end(polygon)-1; it!=std::begin(polygon)-1; --it) {
  // do something with *it
}

5

ใน C ++ 11

ฉันจะใช้อัลกอริทึมทั่วไปfor_eachเพื่อหลีกเลี่ยงการค้นหาชนิดที่ถูกต้องของตัววนซ้ำและการแสดงออกแลมบ์ดาเพื่อหลีกเลี่ยงการใช้งานฟังก์ชัน / วัตถุที่มีชื่อพิเศษ

ตัวอย่างสั้น ๆ "สวย" สำหรับกรณีเฉพาะของคุณ (สมมติว่ารูปหลายเหลี่ยมเป็นเวกเตอร์ของจำนวนเต็ม):

for_each(polygon.begin(), polygon.end(), [&sum](int i){ sum += i; });

ทดสอบเมื่อ: http://ideone.com/i6Ethd

อย่าลืมที่จะรวมถึง:อัลกอริทึมและแน่นอนเวกเตอร์ :)

Microsoft มีตัวอย่างที่ดีในเรื่องนี้จริง ๆ :
source: http://msdn.microsoft.com/en-us/library/dd293608.aspx

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() 
{
   // Create a vector object that contains 10 elements.
   vector<int> v;
   for (int i = 1; i < 10; ++i) {
      v.push_back(i);
   }

   // Count the number of even numbers in the vector by 
   // using the for_each function and a lambda.
   int evenCount = 0;
   for_each(v.begin(), v.end(), [&evenCount] (int n) {
      cout << n;
      if (n % 2 == 0) {
         cout << " is even " << endl;
         ++evenCount;
      } else {
         cout << " is odd " << endl;
      }
   });

   // Print the count of even numbers to the console.
   cout << "There are " << evenCount 
        << " even numbers in the vector." << endl;
}

4
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it; 

2
สำหรับเวกเตอร์นี่เป็นสิ่งที่ดี แต่โดยทั่วไปมันจะดีกว่าถ้าใช้ ++ แทนที่จะเป็น ++ ในกรณีที่ตัววนซ้ำตัวเองนั้นไม่สำคัญ
Steve Jessop

โดยส่วนตัวแล้วฉันคุ้นเคยกับการใช้ ++ i แต่ฉันคิดว่าคนส่วนใหญ่ชอบสไตล์ i ++ (ค่าเริ่มต้นคือข้อมูลโค้ด VS สำหรับ "สำหรับ" คือ i ++) แค่คิด
Mehrdad Afshari

@MehrdadAfshari ใครสนใจว่า "คนส่วนใหญ่" ทำอะไร "คนส่วนใหญ่" ผิดเกี่ยวกับสิ่งต่าง ๆ มากมาย Post-inc / decrement ที่ไม่เคยใช้ค่าก่อนนั้นผิดและไม่มีประสิทธิภาพอย่างน้อยในทางทฤษฎี - โดยไม่คำนึงถึงความถี่ที่จะใช้สุ่มสี่สุ่มห้าในรหัสตัวอย่างย่อยทุกที่ คุณไม่ควรส่งเสริมการปฏิบัติที่ไม่ดีเพียงเพื่อทำให้สิ่งต่าง ๆ ดูคุ้นเคยกับผู้ที่ยังไม่รู้จักดี
underscore_d

2

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


2
ฉันคิดว่ามันเป็นตัวเลือกที่น่ากลัวที่จะถูกเพิกเฉย - มันง่ายต่อการแก้ไขและอีกครั้งในขณะที่ข้อบกพร่องของแท้เกิดขึ้นเนื่องจากข้อผิดพลาดเมื่อเปรียบเทียบค่าที่ลงชื่อ / ไม่ได้ลงชื่อไม่เหมาะสม ตัวอย่างเช่นในกรณีนี้ถ้าขนาดใหญ่กว่า INT_MAX การวนซ้ำจะไม่สิ้นสุด
464 Steve Jessop

... หรืออาจจะยุติทันที หนึ่งในสอง ขึ้นอยู่กับว่าค่าที่ลงนามแล้วจะถูกแปลงเป็นไม่ได้ลงชื่อเพื่อทำการเปรียบเทียบหรือไม่ได้ลงนามแล้วจะถูกแปลงเป็นเซ็นชื่อ บนแพลตฟอร์ม 64 บิตที่มี 32 บิต int แต่เช่น win64 int จะได้รับการเลื่อนระดับเป็น size_t และลูปจะไม่สิ้นสุด
Steve Jessop

@SteveJessop: คุณไม่สามารถพูดได้ด้วยความมั่นใจว่าการวนซ้ำไม่สิ้นสุด ในการทำซ้ำเมื่อi == INT_MAXนั้นi++ทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด ณ จุดนี้สิ่งที่สามารถเกิดขึ้นได้
Ben Voigt

@BenVoigt: จริงและยังคงไม่ได้ให้บริเวณที่จะไม่สนใจคำเตือน :-)
สตีฟเจสซอพ

2

พิจารณาว่าคุณต้องทำซ้ำหรือไม่

<algorithm>หัวมาตรฐานให้เรามีสิ่งอำนวยความสะดวกสำหรับการนี้:

using std::begin;  // allows argument-dependent lookup even
using std::end;    // if the container type is unknown here
auto sum = std::accumulate(begin(polygon), end(polygon), 0);

ฟังก์ชั่นอื่น ๆ ในห้องสมุดอัลกอริทึมทำงานทั่วไป - ตรวจสอบให้แน่ใจว่าคุณรู้ว่ามีอะไรที่พร้อมใช้งานถ้าคุณต้องการประหยัดความพยายาม


1

ชัดเจน แต่มีรายละเอียดที่สำคัญ: ถ้าคุณพูดว่า "for (auto it)" ดังต่อไปนี้คุณจะได้รับสำเนาของวัตถุไม่ใช่องค์ประกอบจริง:

struct Xs{int i} x;
x.i = 0;
vector <Xs> v;
v.push_back(x);
for(auto it : v)
    it.i = 1;         // doesn't change the element v[0]

ในการแก้ไของค์ประกอบของเวกเตอร์คุณต้องกำหนดตัววนซ้ำเป็นข้อมูลอ้างอิง:

for(auto &it : v)

1

หากคอมไพเลอร์ของคุณรองรับคุณสามารถใช้ช่วงเพื่อเข้าถึงองค์ประกอบเวกเตอร์:

vector<float> vertices{ 1.0, 2.0, 3.0 };

for(float vertex: vertices){
    std::cout << vertex << " ";
}

พิมพ์: 1 2 3 หมายเหตุคุณไม่สามารถใช้เทคนิคนี้ในการเปลี่ยนองค์ประกอบของเวกเตอร์


0

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

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


จำนวนเต็มที่ไม่ได้ลงทะเบียนสำหรับขนาด () ไม่จำเป็นต้องเท่ากับ "unsigned int" ในคำศัพท์ C ++ ซึ่งมักจะเป็น 'จำนวนเต็มที่ไม่ได้ลงชื่อ' ในกรณีนี้คือจำนวนเต็ม 64 บิตที่ไม่ได้ลงชื่อในขณะที่ 'unsigned int' โดยทั่วไปคือ 32 บิต
Medran

0

การเพิ่มสิ่งนี้ในขณะที่ฉันไม่พบมันกล่าวถึงในคำตอบใด ๆ : สำหรับการทำซ้ำตามดัชนีเราสามารถใช้decltype(vec_name.size())ซึ่งจะประเมินstd::vector<T>::size_type

ตัวอย่าง

for(decltype(v.size()) i{ 0 }; i < v.size(); i++) {
    /* std::cout << v[i]; ... */
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.