about
Bits Iter C++
05/31/2023
Bits_Iter C++
code, output, and build for C++ on Windows, macOS, and Linux
Synopsis:
This Bit demonstrates uses of C++ iterators to walk through enumerable collections. The purpose is to quickly acquire some familiarity with C++ iteration.- C++ Iterators are smart pointers that are provided by, and have special knowledge about the structure of, C++ iterable containers.
-
An iterable C++ container provides
begin() andend() methods which return iterators pointing to the first and one past the last element of the container.
Demo Notes
1.0 CodeSnaps
Source Code - Bits_Iter.cpp
/*------------------------------------------------------------------- Bits_Iter.cpp - defines functions to iterate over collections - depends on Points.h to provide user-defined point class - depends on Analysis.h for several display and analysis functions */ #include <iostream> // std::cout #include <memory> // std::unique_ptr #include <vector> // vector<T> class #include <array> // array<T> class #include <map> // map<K,V> class #include <set> // set<T> class #include "AnalysisIter.h" // Analysis functions #include "PointsIter.h" // PointN<T> class declaration /* This demo uses the std::string and std::vector<T> classes and a user-defined class, PointN<T>, to illustrate how types support indexing and iteration. Operations: All the classes discussed here provide operations for: T t2 = t1 // copy construction T t3 = temporary // move construction t1 = t2 // copy assignment t3 = temporary // move assignment All instances return their resources when they go out of scope by implicitly calling their destructor. Primitive types can all be copied. Most library and user-defined types can be copied, moved, and deleted by providing member constructors and destructor. Often compiler generation works well, but for classes with pointer members developers must provide them. Processing: All types are static, operations run as native code, and no garbage collection is needed. Resources are returned at end of their declaration scope. */ #pragma warning(disable: 4984) // warns about C++17 extension /*----------------------------------------------- alias type name - pU<T> is the same type as std::unique_ptr<T> - this just provides a shorter name */ template<typename T> using pU = std::unique_ptr<T>; /*----------------------------------------------- forLoopVec accepts std::vector<T> instances by constant reference. - uses range-for to display PointN coordinates */ template<typename T> void forLoopVec(const std::vector<T>& v) { std::cout << "\n "; for(auto const &item : v) { std::cout << item << " "; } } /*----------------------------------------------- forLoopPoint accepts PointN<T> instances by constant reference. - uses range-for to display PointN coordinates */ template<typename T> void forLoopPoint(const PointN<T>& p) { std::cout << "\n "; for(auto const &item : p) { std::cout << item << " "; } } /*----------------------------------------------- whilerPoint accepts PointN<T> instances by constant reference. - uses iterator to display PointN coordinates */ template<typename T> void whilerPoint(const PointN<T>& p) { auto itr = p.begin(); std::cout << "\n " << *itr++; while (itr < p.end()) { std::cout << ", " << *itr++; } } /*----------------------------------------------- whiler is flexible function that accepts any iterable container - will use iterator on C. */ template<typename C> void whiler(const C& c) { auto itr = c.begin(); // uses const overload std::cout << "\n " << *itr++; while (itr != c.end()) { std::cout << ", " << *itr++; } } /*----------------------------------------------- whiler_guarded is flexible function that accepts any container - if C is iterable will use iterator on C. - If non-iterable input is detected, will display error msg and return. - decision is made at compile time. - is_iterable_v is defined in Analysis.h - max is the maximum number of items to show on one line */ template<typename C> void whiler_guarded( const C& c, const std::string& name, size_t indent = 2, size_t max = 8 ) { if constexpr(!is_iterable_v<C>) { // decision at compile-time std::cout << "\n whiler input type is not iterable\n"; return; } else { std::cout << formatColl(c, name, "", indent, max); /* Analysis::formatColl uses range-based for loop to iterate over collection. */ } } /*------------------------------------------------------------------- Demonstration starts here */ void testFormat(); int main() { print("Demonstrate C++ Iteration\n"); showNote("std library types string and vector<T>"); /* create and display std::string object */ auto str = std::string("\"Wile E. Coyote\""); auto out = std::string("contents of str = ") + str; print(out); print("--- showType(str, \"str\"); ---"); showType(str, "str", nl); showNote("Iterate over string"); whiler_guarded(out, "out"); print(); /* create and display std::vector<double> */ auto vec = std::vector<double>{ 3.5, 3, 2.5, 2 }; std::cout << vec; showOp("showType(vec, \"vec\");"); showType(vec, "vec", nl); showOp("vec[2] = -2.5;"); vec[2] = -2.5; std::cout << "\n vec:" << vec; showOp("auto vec2 = vec : copy construction"); /* copy construction */ auto vec2 = vec; std::cout << "\n vec2:" << vec2; showOp("vec2[0] = 42;"); vec2[0] = 42; std::cout << "\n vec2: " << vec2; std::cout << "\n vec: " << vec; showNote( "Copy construction, auto vec2 = vec, creates\n " "independent instance. So changing target vec2\n " "has no affect on source vec." ); showNote( "Iterate over vector" ); showOp("function using range-for taking vector"); forLoopVec(vec); showOp("function using iterator taking iterable container"); whiler(vec); print(); showNote("user-defined type PointN<T>"); PointN<double> p1(5); p1.show("p1"); showNote( "p1.coords() = std::vector<double>\n " "{ 1.0, -2.0, 3.0, 4.5, -42.0 }" ); p1.coords() = std::vector<double>{1.0, -2.0, 3.0, 4.5, -42.0 }; p1.show("p1"); #pragma region showOp("showType(p1, \"p1\", nl);"); showType(p1, "p1", nl); std::cout << " p1.coords()[2] = " << p1.coords()[2] << "\n"; showNote( "iterate over PointN<T>" ); showOp("function using range-for taking Point"); forLoopPoint(p1); showOp("function using iterator taking Point"); whilerPoint(p1); showOp("function using iterator taking iterable container"); whiler_guarded(p1, "p1"); showOp("same function attempting to take non-iterable"); struct S { int i; }; auto s = S{3}; whiler_guarded(s, "s"); print(); showNote("heap-based string instance"); /* standard library type std::string */ /* uses alias pU for std::unique_ptr, defined above */ showOp( "pU<std::string> " "pStr(new std::string(\"\\\"Road Runner\\\"\")" ); pU<std::string> pStr(new std::string("\"Road Runner\"")); std::cout << "\n pStr contents = " << *pStr << "\n"; showOp("showType(*pStr, \"*pStr\")"); showType(*pStr, "*pStr", nl); /* std::unique_ptr<T> cannot be copied but can be moved */ showOp("showType(move(pStr), \"pStr\")"); showType(move(pStr), "pStr", nl); /* standard library type std::vector<T> */ showNote("heap-based vector instance"); showOp( "pU<std::vector<double>>\n " " pVec(new std::vector<double>{ 1.5, 2.5, 3.5 });" ); pU<std::vector<double>> pVec( new std::vector<double>{ 1.5, 2.5, 3.5 } ); print(); showOp("iterating over *pVec"); whiler_guarded(*pVec, "*pVec"); print(); std::cout << "\n *pVec = " << *pVec; showType(*pVec, "*pVec", nl); std::cout << "\n pVec = " << pVec; showType(move(pVec), "move(pVec)", nl); /* custom point type */ showNote("heap-based PointN instance"); showOp("pU<PointN<double>> pPointN(new PointN<double>(4))"); pU<PointN<double>> pPointN(new PointN<double>(4)); pPointN->show("*pPointN"); showOp( "pPointN->coords() = \n" " std::vector<double>{ 1.0, 3.5, -2.0, 42.0 };" ); pPointN->coords() = std::vector<double>{ 1.0, 3.5, -2.0, 42.0 }; pPointN->show("*pPointN"); std::cout << "\n value of pPointN->coords()[1] is " << pPointN->coords()[1]; showOp("showType(*pPointN, \"*pPointN\");"); showType(*pPointN, "*pPointN"); print(); showOp("iterating over *pPointN"); whiler_guarded(*pPointN, "*pPointN"); print(); showOp("showType(std::move(pPointN), \"pPointN\");"); showType(std::move(pPointN), "pPointN"); /* pPointN moved, so now invalid */ print(); showNote( "Iterate over map, using Analysis::format" ); std::map<std::string, int> map{{"zero", 0}, {"one", 1}}; map.insert({"two", 2}); std::cout << format(map, "map"); // #define TEST #ifdef TEST testFormat(); #endif print("\n That's all Folks!\n\n"); } void testFormat() { showNote("Test and demonstrate formatting functions"); showOp("demonstrate PointN show()"); print("default indent = 4 and width = 7:"); PointN<int> p2(15); p2.show("p2"); size_t saveLeft = p2.left(); size_t saveWidth = p2.width(); print("\n indent = 6, width = 12:"); p2.left() = 6; p2.width() = 12; p2.show("p2"); showOp( "demonstrate operator<< overload for PointN ---" ); p2.left() = saveLeft; p2.width() = saveWidth; print("default indent = 4 and width = 7:"); std::cout << p2; print("\n indent = 6, width = 12:"); p2.left() = 6; p2.width() = 12; std::cout << p2; showOp( "demonstrate operator<< overload for vector" ); auto vtest = std::vector<int>{1, 2, 3, 4, 5, 6, 7, 8, 9 }; print("default indent = 4 and width = 7:"); std::cout << vtest; DisplayParams.left = 2; DisplayParams.width = 5; print("indent = 2, width = 5:"); std::cout << vtest; std::cout << formatColl(vtest, "vtest", nl, 2, 5); std::cout << formatColl(vtest, "vtest", nl, 4, 7); std::cout << formatColl(vtest, "vtest", nl, 2, 9); std::cout << formatColl(vtest, "vtest: vector<int>", nl, 2, 10); std::array<double, 5> arrtest = { 1, 2, 3, 4.5, -3.14159 }; std::cout << formatColl(arrtest, "arrtest", nl, 2, 4); std::map<int, std::string> amap { {1, "one"}, {2, "two"}, {3, "three"} }; std::cout << formatColl(amap, "amap", nl, 2, 4); std::set<std::string> aset { "one", "two", "three", "four", "five" }; std::cout << formatColl(aset, "aset", nl, 2, 4); std::string astring = "this is a string"; std::cout << formatString(astring, "astring", nl, 2); double adouble { 3.1415927 }; std::cout << formatScalar(adouble, "adouble", nl); showNote("Using consolidated format function", nl); std::cout << format(adouble, "adouble", nl); std::cout << format(astring, "astring", nl); std::vector<double> avec{ 1, 2, 3, 4.5, -3.14159 }; std::cout << format(avec, "avec", nl); std::cout << format(amap, "amap", nl); }
Source Code - PointsIter.h
/*------------------------------------------------------------------- PointsIter.h defines point class PointN<T> - PointN<T> represents points with many coordinates of unspecified type T - Added iterator, begin(), end() */ #include <iostream> #include <vector> /*------------------------------------------------------------------- PointN<T> class represents a point in an n-Dimensional hyperspace. It uses a template parameter to support a variety of coordinate types, and uses a vector to hold any finite number of coordinates. Its default constructor PointN() is declared delete so it won't be provided here nor generated by the compiler. It provides iterators and begin() and end() members. These are used for iterating over PointN<T>'s elements. */ template<typename T> class PointN { public: using iterator = typename std::vector<T>::iterator; using const_iterator = typename std::vector<T>::const_iterator; using value_type = T; PointN() = delete; // default ctor PointN(size_t N); PointN(const PointN& pt) = default; // copy ctor PointN(PointN&& pt) = default; // move ctor PointN& operator=(const PointN& pt) = default; // copy assignment PointN& operator=(PointN&& pt) = default; // move assignemnt ~PointN() = default; // dtor iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; size_t size() const; T& operator[](size_t index); T operator[](size_t index) const; void push_back(T r); T pop_back(); std::vector<T>& coords() { return coord; } void show(const std::string& name); // display contents size_t& left() { return _left; }; // display indent size_t& width() { return _width; }; // display width private: std::vector<T> coord; size_t _left = 2; // default display indent size_t _width = 7; // default display row width }; /*----------------------------------------------- PointN<T> constructor with size */ template<typename T> PointN<T>::PointN(size_t N) { for(size_t i=0; i<N; i++) { coord.push_back(T{0}); } } template<typename T> size_t PointN<T>::size() const { return coord.size(); } template<typename T> T& PointN<T>::operator[](size_t index) { if (index < 0 || coord.size() <= index) { throw "indexing error"; } return coord[index]; } template<typename T> T PointN<T>::operator[](size_t index) const { if (index < 0 || coord.len() <= index) { throw "indexing error"; } return coord[index]; } template<typename T> typename PointN<T>::iterator PointN<T>::begin() { return coord.begin(); } template<typename T> typename PointN<T>::iterator PointN<T>::end() { return coord.end(); } template<typename T> typename PointN<T>::const_iterator PointN<T>::begin() const { return coord.begin(); } template<typename T> typename PointN<T>::const_iterator PointN<T>::end() const { return coord.end(); } template<typename T> void PointN<T>::push_back(T t) { coord.push_back(t); } template<typename T> T PointN<T>::pop_back() { return coord.pop_back(); } /*----------------------------------------------- PointtN<T> display function */ template<typename T> void PointN<T>::show(const std::string& name) { std::cout << "\n" << indent(_left) << name << ": " << "PointN<T>"; std::cout << " {\n"; std::cout << fold(coord, _left + 2, _width); std::cout << indent(_left) << "}"; } /*----------------------------------------------- Overload operator<< required for showType(PointN<T> t, const std::string& nm) */ template<typename T> std::ostream& operator<<(std::ostream& out, PointN<T>& t2) { out << "\n" << indent(t2.left()) << "PointN<T>"; out << " {\n"; out << fold(t2.coords(), t2.left() + 2, t2.width()); out << indent(t2.left()) << "}"; return out; }
Source Code - AnalysisIter.h
/*------------------------------------------------------------------- AnalysisIter.h - Provides functions that analyze types, display results and other program defined information. - Some of this code requires complex template operations. Those will be discussed in the generics bit. - You can skip the hard parts until then, without loss of understanding. */ #include <typeinfo> // typeid #include <utility> // move() #include <sstream> // stringstream #include <type_traits> // is_scalar, if constexpr #include <iostream> // cout #include <vector> // vector /*------------------------------------------------------------------- Analysis function declarations are provided here so that definitions below may be placed in any order. That's needed because C++ requires declaration before use. */ template<typename T> void showType(T t, const std::string &nm, const std::string& suffix = ""); void showNote(const std::string& txt, const std::string& suffix = ""); void showOp(const std::string& opstr, const std::string& suffix = ""); void print(const std::string& txt = ""); void println(const std::string& txt = ""); std::string truncate(size_t N, const char* pStr); std::string indent(size_t n); template<typename T> std::string fold(std::vector<T>& v, size_t left, size_t width); template<typename T> std::string formatColl( const T& t, const std::string& nm, const std::string& suffix = "", size_t left = 2, size_t width = 7 ); template<typename T> std::string formatScalar( const T& t, const std::string& nm, const std::string& suffix = "", size_t left = 2 ); template<typename T> std::string formatString( const T& t, const std::string& nm, const std::string& suffix, size_t left = 2 ); template<typename T> std::string format( const T& t, const std::string& nm, const std::string& suffix = "", size_t left = 2, size_t width = 7 ); /* end of function declarations -------------------------------------------------------------------*/ /*------------------------------------------------------------------- Display and Analysis function and globals definitions --------------------------------------------------------------------- */ const std::string nl = "\n"; /*------------------------------------------------------------------- Mutable globals are a common source of bugs. We try not to use them, but will use DisplayParams here to control how the insertion operator sends instances to standard output. */ struct displayParams { size_t left = 2; // number of spaces to indent size_t width = 7; // width of display row size_t trunc = 40; // replace text after trunc with ... } DisplayParams; // global object /*----------------------------------------------- Overload operator<< required for showType(std::vector<T> v, const std::vector<T>& nm) */ template<typename T> std::ostream& operator<<(std::ostream& out, std::vector<T>& v) { out << format(v, "vector<T>", "", DisplayParams.left, DisplayParams.width); return out; } /*----------------------------------------------- Display calling name, static class, and size */ template<typename T> void showType(T t, const std::string &callname, const std::string& suffix) { std::cout << "\n " << callname; // show name at call site std::cout << " type: " << truncate(DisplayParams.trunc,typeid(t).name()); // show type std::cout << "\n size: " << sizeof(t); // show size on stack std::cout << suffix; } /*----------------------------------------------- Display emphasized text */ inline void showNote(const std::string& txt, const std::string& suffix) { print("--------------------------------------------------"); print(" " + txt); print("--------------------------------------------------"); std::cout << suffix; } /*----------------------------------------------- Display emphasized line */ inline void showOp(const std::string& opstr, const std::string& suffix) { std::cout << "\n --- " << opstr << " ---" << suffix; } /*----------------------------------------------- Helper function for formatting output - truncates line to N chars and adds ellipsis */ inline std::string truncate(size_t N, const char* pStr) { std::string temp(pStr); if(temp.length() > N) { temp.resize(N); return temp + "..."; } return temp; } /*----------------------------------------------- Helper function for formatting output - generates string of n blanks to offset text */ inline std::string indent(size_t n) { return std::string(n, ' '); } /*----------------------------------------------- Helper function for formatting output - folds lines after width elements */ template<typename T> std::string fold(std::vector<T>& v, size_t left, size_t width) { std::stringstream out("\n"); out << indent(left); for(int i=0; i<v.size(); ++i) { if((i % width) == 0 && i != 0 && i != width - 1) { out << "\n" << indent(left); } if(i < v.size() - 1) { out << v[i] << ", "; } else { out << v[i] << "\n"; break; } } return out.str(); } /*----------------------------------------------- Helper function for formatColl - defines out << std::pair<K,V> - used in formatColl for associative containers */ template<typename K, typename V> std::stringstream& operator<<( std::stringstream& out, const std::pair<K,V>& p ) { out << "{" << p.first << ", " << p.second << "}"; return out; } /*----------------------------------------------- Format output for Collection types - any type with begin() and end() like all the STL containers. */ template<typename Coll> std::string formatColl( const Coll& c, const std::string& nm, const std::string& suffix, size_t left, size_t width ) { std::stringstream out; out << "\n" << indent(left) << nm << ": {\n" << indent(left + 2); size_t i = 0; for(const Coll::value_type& elem : c) { if((i % width) == 0 && i != 0 && i != width - 1) { out << "\n" << indent(left + 2); } if(i < c.size() - 1) { out << elem << ", "; } else { out << elem << "\n" << indent(left) << "}" << suffix; break; } ++i; } return out.str(); } /*----------------------------------------------- Format output for scalar types like primitives */ template<typename T> std::string formatScalar( const T& t, const std::string& nm, const std::string& suffix, size_t left ) { std::stringstream out; out << "\n" << indent(left) << nm << ": " << t << suffix; return out.str(); } /*----------------------------------------------- Format output for strings - indent and embed in quotation marks */ template<typename T> std::string formatString( const T& t, const std::string& nm, const std::string& suffix, size_t left ) { std::stringstream out; out << "\n" << indent(left) << nm << ": \"" << t << "\"" << suffix; return out.str(); } /*----------------------------------------------- Defines is_iterable trait - detects STL containers and user-defined types that provide iteration - uses template metaprogramming, e.g., user code that runs at compile-time https://stackoverflow.com/questions/13830158/check-if-a-variable-type-is-iterable */ template <typename T, typename = void> struct is_iterable : std::false_type {}; // this gets used only when we can call // std::begin() and std::end() on that type template <typename T> struct is_iterable< T, std::void_t <decltype(std::begin(std::declval<T>())), decltype(std::end(std::declval<T>()))> > : std::true_type {}; template <typename T> constexpr bool is_iterable_v = is_iterable<T>::value; /*----------------------------------------------- Displays almost everything. - strings work better with formatString(...) https://www.cppstories.com/2018/03/ifconstexpr/ Iteration is discussed in Bit Cpp_iter */ template<typename T> std::string format( const T& t, const std::string& nm, const std::string& suffix, size_t left, size_t width ) { if constexpr(is_iterable_v<T>) { // decision at compile-time return formatColl(t, nm, suffix, left, width); } else { return formatScalar(t, nm, suffix, left); } } /*----------------------------------------------- Display text after newline and indentation */ inline void print(const std::string& txt) { std::cout << "\n " << txt; } /*----------------------------------------------- Display text after newline and indentation - provides trailing newline */ inline void println(const std::string& txt) { std::cout << "\n " << txt << "\n"; }
Output
C:\github\JimFawcett\Bits\Cpp\Cpp_Iter\build > debug/Cpp_Iter Demonstrate C++ Iteration -------------------------------------------------- std library types string and vector<T> -------------------------------------------------- contents of str = "Wile E. Coyote" --- showType(str, "str"); --- str type: class std::basic_string<char,struct std:... size: 40 -------------------------------------------------- Iterate over string -------------------------------------------------- out: { c, o, n, t, e, n, t, s, , o, f, , s, t, r, , =, , ", W, i, l, e, , E, ., , C, o, y, o, t, e, " } vector<T>: { 3.5, 3, 2.5, 2 } --- showType(vec, "vec"); --- vec type: class std::vector<double,class std::allo... size: 32 --- vec[2] = -2.5; --- vec: vector<T>: { 3.5, 3, -2.5, 2 } --- auto vec2 = vec : copy construction --- vec2: vector<T>: { 3.5, 3, -2.5, 2 } --- vec2[0] = 42; --- vec2: vector<T>: { 42, 3, -2.5, 2 } vec: vector<T>: { 3.5, 3, -2.5, 2 } -------------------------------------------------- Copy construction, auto vec2 = vec, creates independent instance. So changing target vec2 has no affect on source vec. -------------------------------------------------- -------------------------------------------------- Iterate over vector -------------------------------------------------- --- function using range-for taking vector --- 3.5 3 -2.5 2 --- function using iterator taking iterable container --- 3.5, 3, -2.5, 2 -------------------------------------------------- user-defined type PointN<T> -------------------------------------------------- p1: PointN<T> { 0, 0, 0, 0, 0 } -------------------------------------------------- p1.coords() = std::vector<double> { 1.0, -2.0, 3.0, 4.5, -42.0 } -------------------------------------------------- p1: PointN<T> { 1, -2, 3, 4.5, -42 } --- showType(p1, "p1", nl); --- p1 type: class PointN<double> size: 48 p1.coords()[2] = 3 -------------------------------------------------- iterate over PointN<T> -------------------------------------------------- --- function using range-for taking Point --- 1 -2 3 4.5 -42 --- function using iterator taking Point --- 1, -2, 3, 4.5, -42 --- function using iterator taking iterable container --- p1: { 1, -2, 3, 4.5, -42 } --- same function attempting to take non-iterable --- whiler input type is not iterable -------------------------------------------------- heap-based string instance -------------------------------------------------- --- pU<std::string> pStr(new std::string("\"Road Runner\"") --- pStr contents = "Road Runner" --- showType(*pStr, "*pStr") --- *pStr type: class std::basic_string<char,struct std:... size: 40 --- showType(move(pStr), "pStr") --- pStr type: class std::unique_ptr<class std::basic_s... size: 8 -------------------------------------------------- heap-based vector instance -------------------------------------------------- --- pU<std::vector<double>> pVec(new std::vector<double>{ 1.5, 2.5, 3.5 }); --- --- iterating over *pVec --- *pVec: { 1.5, 2.5, 3.5 } *pVec = vector<T>: { 1.5, 2.5, 3.5 } *pVec type: class std::vector<double,class std::allo... size: 32 pVec = 000002965C6829B0 move(pVec) type: class std::unique_ptr<class std::vector<... size: 8 -------------------------------------------------- heap-based PointN instance -------------------------------------------------- --- pU<PointN<double>> pPointN(new PointN<double>(4)) --- *pPointN: PointN<T> { 0, 0, 0, 0 } --- pPointN->coords() = std::vector<double>{ 1.0, 3.5, -2.0, 42.0 }; --- *pPointN: PointN<T> { 1, 3.5, -2, 42 } value of pPointN->coords()[1] is 3.5 --- showType(*pPointN, "*pPointN"); --- *pPointN type: class PointN<double> size: 48 --- iterating over *pPointN --- *pPointN: { 1, 3.5, -2, 42 } --- showType(std::move(pPointN), "pPointN"); --- pPointN type: class std::unique_ptr<class PointN<doubl... size: 8 -------------------------------------------------- Iterate over map, using Analysis::format -------------------------------------------------- map: { {one, 1}, {two, 2}, {zero, 0} } That's all Folks! C:\github\JimFawcett\Bits\Cpp\Cpp_Iter\build >
Build
C:\github\JimFawcett\Bits\Cpp\Cpp_Iter > cd build C:\github\JimFawcett\Bits\Cpp\Cpp_Iter\build > cmake .. -- Selecting Windows SDK version 10.0.22000.0 to target Windows 10.0.22621. -- Configuring done -- Generating done -- Build files have been written to: C:/github/JimFawcett/Bits/Cpp/Cpp_Iter/build C:\github\JimFawcett\Bits\Cpp\Cpp_Iter\build > cmake --build . MSBuild version 17.5.1+f6fdcf537 for .NET Framework Checking Build System Building Custom Rule C:/github/JimFawcett/Bits/Cpp/Cpp_Iter/CMakeLists.txt Cpp_Iter.vcxproj -> C:\github\JimFawcett\Bits\Cpp\Cpp_Iter\build\Debug\Cpp_Iter.ex e Building Custom Rule C:/github/JimFawcett/Bits/Cpp/Cpp_Iter/CMakeLists.txt C:\github\JimFawcett\Bits\Cpp\Cpp_Iter\build >
2.0 VS Code View
3.0 References
Reference | Description |
---|---|
C++ Story: Standard Template Library | Covers containers, iterators, and STL algorithms |
C++ Story | E-book with thirteen chapters covering most of intermediate C++ |
C++ Bites | Relatively short feature discussions |