Container questions

Pages: 123
Just read and practiced my vector/deque/list/forward_list/set/multiset containers and I wondered about this one.

1) Why did they make it so that a set is REQUIRED to have a struct/class with operator() for sort (such as for descending) and meanwhile a list can just have a binary predicate function for sorting? They could have made the set containers use binary predicates functions too, right or just made both use operator() in structs/classes, right?
I just played around with this to see what might be implicit conversion and what else might work. Seems not to be much deviation from the requirements, but why is this so?

2) What the heck is going on with this site? There used to be a lot more posters? Is AI taking over C++ or are they not teaching C++ anymore? Has C++ been supplanted by something better and just as fast now?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include<iostream>
#include<set>
#include<list>
using namespace std;


template<typename T>
void Display(const T& container)
{
	//typename T::const_iterator element;
	//for (element = container.cbegin(); element != container.cend(); ++element)
	for (auto element = container.cbegin(); element != container.cend(); ++element)
	{
		size_t pos = distance(container.cbegin(), element);
		cout << "[" << pos << "] = " << *element << endl;
	}
	cout << "SIZE = " << container.size() << endl;
	cout << "*****************" << endl;
}

//For set<int>...WORKS!!!
template<typename T>
//class SortDescend	//WORKS w class too!!!
struct SortDescend
{
	public:
	bool operator()(const T& lhs, const T& rhs) const
	{
		return (lhs > rhs);
	}
};

////For set<int>...DOES NOT WORK!!!
//template<typename T>
//	bool SortDescend(const T& lhs, const T& rhs)
//	{
//		return (lhs > rhs);
//	}


////For set<int>...DOES NOT WORK!!!
//template<typename T>
//	bool operator()(const T& lhs, const T& rhs) const
//	{
//		return (lhs > rhs);
//	}


//For list<int>...WORKS!!!
template<typename T>
bool SortDescendList(const T& lhs, const T& rhs)
{
	return (lhs > rhs);
}

////For list<int>...DOES NOT WORK with a struct!!!
//template<typename T>
//struct SortDescendList
//{
//	bool operator()(const T& lhs, const T& rhs) const
//	{
//		return (lhs > rhs);
//	}
//};

int main()
{
	//Auto sort ascending (no duplicates)
	set<int> s1{ 2,5,1,3,2,1,4 };	
	Display(s1);

	//WORKS!!!
	set<int, SortDescend<int>> s2(s1.cbegin(), s1.cend());	
	Display(s2);   

	//NOT WORK w binary predicate, need struct or class!!!
	//set<int, SortDescend<int>> s2(s1.cbegin(), s1.cend());	
	//Display(s2);   
	
	//NOT WORK w operator() directly, need struct or class!!!
	//set<int, operator()<int>> s2(s1.cbegin(), s1.cend());		
	//Display(s2);  

	//WORKS!!!  Auto sort ascending (duplicates)
	multiset<int> ms1{ 2,5,1,3,2,1,4 };
	Display(ms1);

	list<int> l1{ 2,5,1,3,2,1,4 };
	Display(l1);

	l1.sort(SortDescendList<int>);	//WORKS w function binary predicate obj
	//l1.sort(SortDescendList<int>);//NOT WORK w struct
	Display(l1);

	return 0;
}
/*
OUTPUT:
[0] = 1
[1] = 2
[2] = 3
[3] = 4
[4] = 5
SIZE = 5
*****************
[0] = 5
[1] = 4
[2] = 3
[3] = 2
[4] = 1
SIZE = 5
*****************
[0] = 1
[1] = 1
[2] = 2
[3] = 2
[4] = 3
[5] = 4
[6] = 5
SIZE = 7
*****************
[0] = 2
[1] = 5
[2] = 1
[3] = 3
[4] = 2
[5] = 1
[6] = 4
SIZE = 7
*****************
[0] = 5
[1] = 4
[2] = 3
[3] = 2
[4] = 2
[5] = 1
[6] = 1
SIZE = 7
*****************
*/
Many schools do not teach c++. They have moved on to much simpler languages that are easier to grade with AI. The big schools do not even grade programs anymore, they just have the students submit their code to a site that then runs the code on an 'unknown**' test data set and then reports back the program's output + whether it successfully passed or failed all the tests. Typically you can resubmit until you get it all right.

** I helped my brother in law deal with this crap a couple years back. You could write a program that echoed the test data back to you in the output and did nothing else. It would fail, but it would give you the inputs that were breaking your code so you could fix it much faster. Also the thing literally gave a 100% A+ grade on hard coded output programs that simply supplied the correct answer. The AI may be smarter now, but it was the most idiotic mess I had ever seen, and this was one of the 'big' name technical universities.

Yes, I 'cheated' to get the input to help him fix his code, but we did the programs right. Was just identification of edge cases stuff, and I didn't have time to figure out what he overlooked when their grader already knew what would trip it up... if they can't be bothered to help or actually look at your code, I can't be bothered to play nice. Case in point, they used java, so the nonstop limitations on what you can and cannot do were also in my way as I tried to help.
Last edited on
> 2) What the heck is going on with this site? There used to be a lot more posters?
Forums like this are a dying breed.

Two I was on regularly closed in the past couple of years. The other three I'm on have such low traffic to be hardly worth bothering with.

The problem is, as soon as a forum falls off the bottom of say the 2nd page of google search results, traffic to the site tanks. Fewer questions means the experts wander off to find better things to do with their time. What posts do show up get lower quality answers - if they get answered at all.

As jonnin says, everyone just wants to copy from one AI bot into another AI bot, and pretend they know something.
1) std::list doesn't need to be sorted. It's something that you choose to do. You can always resort the list in another order later if you want.

The callable that you pass to sort can be a function but you can also pass "functors" (function objects). A functor is an object that overloads the call operator (i.e. operator()). The advantage of using a functor is that they can contain "state" (variables). This is what lambda expressions give you because they need to be able to store the captured variables somewhere.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
list<int> l = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

struct DistanceCmp
{
	int k;
	bool operator()(int a, int b) const
	{
		return std::abs(k - a) < std::abs(k - b);
	}
};


// Sort the elements based on the "distance" from the value 4
l.sort(DistanceCmp{4});
Display(l);

// Sort the elements based on the "distance" from the value 15
l.sort(DistanceCmp{15});
Display(l);
[0] = 4
[1] = 3
[2] = 5
[3] = 2
[4] = 6
[5] = 1
[6] = 7
[7] = 8
[8] = 9
[9] = 10
SIZE = 10
*****************
[0] = 10
[1] = 9
[2] = 8
[3] = 7
[4] = 6
[5] = 5
[6] = 4
[7] = 3
[8] = 2
[9] = 1
SIZE = 10
*****************

Note that the ability to accept any callable (function pointer or functor) comes for free when implementing a function template because they use the same syntax.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

template <typename F>
void call_twice(F f)
{
	f();
	f();
}

void print()
{
	std::cout << "Hello!\n";
}

int main()
{
	call_twice(print);
	
	int x = 10;
	call_twice([&x](){ std::cout << ++x << "\n"; });
}
Hello!
Hello!
11
12

std::set is a bit different because it always needs to be able to compare the elements. That's what allows it to store them in a way that makes lookups efficient. That you're able to loop over the elements in sorted order is just a side-effect of this that you might want to take advantage of sometimes but it's not the main reason why it needs a "comparator".

The comparator is a template argument to std::set and is therefore considered part of the type (e.g. std::set<int,std::less<int>> and std::set<int,std::greater<int>> are two different types). This makes sense because you're not supposed to change the comparator dynamically at runtime. It also means the set does not need to store the comparator, instead it can just construct it when necessary, which saves space. It also allows the compiler to inline the comparison and optimize the code better because it knows the implementation, something that would generally not be possible if it used a function pointer.

If you were to try passing a function as the second template argument to std::set (e.g. set<int, my_cmp_fun>) it doesn't work because it expects the comparator to be a type and functions are not types. If you instead pass the pointer type of the function (e.g. set<int, bool(*)(int,int)>) it might compile but it won't work correctly because you haven't specified the function to use and std::set has not been designed to handle this situation.


2) The email feature doesn't work so there is no way for new users to verify their accounts. What the others say about forums like these being less popular nowadays is also true. Sadly people tend to hang out on non-niche sites like Facebook and Reddit.

r/cpp_questions is quite active:
https://www.reddit.com/r/cpp_questions/

There is also r/cpp but it's not for "questions":
https://www.reddit.com/r/cpp/
Last edited on
@SubZeroWins. Small point but you don't need public in a struct (L26). All are public by default. For class all are private by default.
2) I can see how AI automation can cause an injustice to the student and impede learning. At the same time one might be temporarily grateful for it during time of essence scenarios, and especially with multiple courses and/or work. I would always prefer the long and hard way to learn, but options during crunch time.

1) Peter, interesting stuff here. Let me take one small step back before I micro-analyze all of your content.
For when comparing a "list" vs "set":

A) inline:
I should not need to inline the binary predicate for a "list", but the compiler will inline it for me if it deems it optimizing, right? I provided the set of elements and so it has everything it needs to optimize and inline it. If so, then both set & list have the same outcomes with inline by the compiler. If the elements require inputs from the user, then both the set and list might not inline and optimize. So as far as I can tell both set and list have the same outcome.

1
2
3
4
5
template<typename T>
inline bool SortDescendList(const T& lhs, const T& rhs)  //NO need to add inline, as compiler can do it
{
	return (lhs > rhs);
}


But yes, if I want to brute force an inline and make sure in case compiler decides otherwise.

B) The addition of the Descending Binary sort predicate weighing down the "set" or "list".

The "set" uses the elements as the actual key to automatically sort in ascending order for a binary tree, and thus needs and requires the std::less binary predicate. I did not look it up and it is probably a struct or class with operator(). Since it NEEDS to sort, then the std::less is used regardless if you want it to or not, so it is weighed down already.
The "list" has its own sort() in ascending default and is weighed down already too.

So, lets focus on the descending addition then.

1
2
set<int, SortDescend<int>> s2{ 2,5,1,3,2,1,4 };  //For set
l1.sort(SortDescendList<int>);	//For list 


So they both get weighed down when I use the SortDescend. So in that respect, they are both the same to me. The only difference is the "set" is a binary predicate operator() within a struct/class. I also understand though that since they used std::less in a way that uses a struct/class with operator(), then they want you to follow a similar protocol and expect you to use the same and they did not want to overload any other options for us.

So then if my assumptions about A & B above are correct, and there is something that I am just not seeing now properly about efficiency, then if it is so efficient and better why didn't they force upon us the usage of operator() in a struct/class for a "list"?

I think I may skip the map chapter for now and focus on the function object chapter. A function object is a functor, does that mean it is a function pointer automatically or only when you send a function as a template type "template<typename F>" that it becomes a function pointer? Still not sure what that really means, but your code sample is amazing and I did not know that was even possible!

seeplus, thanks! I actually knew that and left it in because I tested it with a class as well. I also had another section commented out, where I did not include public:.

class: default private, with default private inheritance
struct: default public, with default public inheritance
union: default public, no inheritance

Funny thing is that I was almost going to comment that line "//for class" since you guys are so good and catch/help with everything. That is why I threw in the "auto element" in there too, rather than writing it out, since I knew this group would spot it.


CPlusPlus is a niche programming site that hasn't seen any updates to areas other than the fora. You want to know about C++17 or later? Gotta go elsewhere.
Each C++ container fits a different need so the interface/requirements are different. If all the containers had the exact same interface and requirements why have different containers?

Look at what each container does.

https://en.cppreference.com/w/cpp/container
I just read the very beginning of Function Objects, and I already see that it is standard and required in more than just the sort on STL's. I can also see that it is more powerful, since the struct or class can contain members, which can make things easier to work with. One can also include aggregation/composition if you include other objects within the Sort struct/class and it can make your life easier with more complex scenarios.

Alright, so why was it not forced on list sorting then?
Thanks George. Yes, there are different scenarios and some I am immediately aware of such as the const time insertion/removal on a vector at the end and the need to reallocate memory if one inserts anywhere but the end. And things like a set providing logarithmic complexity and benefits to quicker and more frequent searches.

I have memorized some of them.
I should not need to inline the binary predicate for a "list", but the compiler will inline it for me if it deems it optimizing, right?

Yeah, that's right. I was talking about the optimization, not the keyword.

If the elements require inputs from the user, then both the set and list might not inline and optimize.

Inlining isn't affected by where the values of the elements come from.

Function pointers and virtual functions are often difficult/impossible to inline because they might end up calling different functions.

If you pass a functor with a non-virtual call operator then it's much simpler because there is only one possible implementation.

So as far as I can tell both set and list have the same outcome.

Yeah, inlining should be equally easy for both set and list::sort if you pass a functor.

If you pass a function pointer to list::sort it wouldn't be able to inline the comparisons inside list::sort, but if the call to list::sort itself is inlined (and the comparison function is known) then it would be possible.

The reason I mentioned inlining was just to try and defend why std::set doesn't store a function pointer.

Note that inlining is not just about avoiding the function call overhead but it also allows the compiler to optimize the code more aggressively for the specific situation where it's called.

Since it NEEDS to sort, then the std::less is used regardless if you want it to or not, so it is weighed down already.

std::less is just the default.

So, lets focus on the descending addition then.
1
2
set<int, SortDescend<int>> s2{ 2,5,1,3,2,1,4 };  //For set
l1.sort(SortDescendList<int>);	//For list  

Note that you can use SortDescend even for list::sort if you want. You just need to create an object of it and pass it as argument.
 
l1.sort(SortDescend<int>());

if it is so efficient and better why didn't they force upon us the usage of operator() in a struct/class for a "list"?

It's convenient to be able to pass a function and, as I said earlier, if the compiler inlines the list::sort call then it can easily inline the comparisons too because the function is right there at the call site. If std::set allowed you to specify the comparator as a function pointer when it was created it would be much harder to do the same because the point where you specify the function and where it gets called is not necessarily close.

But don't focus too much on the performance argument.

- You specify the comparator for std::set by passing the comparator type as a template argument but functions/function pointers cannot be passed as template arguments.

- list::sort takes a callable and doesn't care if it's a function pointer or a functor as long as it can be invoked with two elements as arguments. It would require more effort to implement a version that only accepts functors but not function pointers.

A function object is a functor, does that mean it is a function pointer automatically or only when you send a function as a template type "template<typename F>" that it becomes a function pointer?

Functor is short for function object. It's the same thing.

When I think about a functor, and the way I have used the word above, is that it's a class object with a function call operator. But technically I guess function pointers are also functors (because pointers are objects) but you need to actually initialize them (default initialization is not enough) which is not possible if you only get to specify the type like with std::set.

If you pass a function to call_twice then F will be the function pointer type.
If you pass an object then F will simply be the type of that object.
Last edited on
The call_twice() program you wrote, what chapters would you normally learn about this in, the Function Obj chapter or the template chapter? I didn't really even know you can send a function as a type, new and amazing to me!

Somehow just sending the "[&x]" gives it the address of the variable x, but more importantly gives it the address of the calling function so that it can call it twice. It does not REALLY do anything with the address of x ("&x"), since the x is in the calling function body, which is in the main already.

What does "[&x]" translate to? I would have thought it translates to a pointer to the address of "&x" and then the [] means get the value of that address location. But then it can't mean just that or else it wouldn't be able to call itself twice. Somehow "[&x]" is the object (i.e. the calling function).

Fascinating, nonetheless.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <vector>
using namespace std;

template <typename F>
void call_twice(F f)
{
	f();
	f();
}

void print()
{
	std::cout << "Hello!\n";
}

int main()
{
	call_twice(print);
	
	int x = 10;
	//call_twice([&x](){ std::cout << ++x << "\n"; });		//WORKS
	//call_twice([](){ std::cout << ++x << "\n"; });		//NOT WORK
	//call_twice(&x(){ std::cout << ++x << "\n"; });		//NOT WORK
	//call_twice((&x)(){ std::cout << ++x << "\n"; });		//NOT WORK
	//call_twice(*&x(){ std::cout << ++x << "\n"; });		//NOT WORK
	//call_twice((*(&x))(){ std::cout << ++x << "\n"; });	//NOT WORK
	
	int* px = &x;
	//call_twice(px(){ std::cout << ++x << "\n"; });		//NOT WORK
	//call_twice([px](){ std::cout << ++x << "\n"; });		//NOT WORK
	cout << "*px = " << *px << endl;
	
	return 0;
}


I understand your intent now of showing that there would be a fundamental efficiency flaw when sending a pointer to a function and avoiding inlining, and so they chose struct/class with operator() for "set".

Strictly speaking though on these two lines taken within the context of my code.
1
2
set<int, SortDescend<int>> s2{ 2,5,1,3,2,1,4 };  //For set
l1.sort(SortDescendList<int>);	//For list   


They are both sent in as pass by value, but are probably not just copied but inlined so that it is more efficient. So efficient that the comparisons themselves are inlined as well. So there is no need to waste time calling multiple functions and it just zips through the code that was sequentially inlined and computed as fast as it possibly can be.
Last edited on
The call_twice() program you wrote, what chapters would you normally learn about this in, the Function Obj chapter or the template chapter?

Which book are you reading? I don't think it's something that is necessarily explained in every book.

The book Programming: Principles and Practice Using C++ by Bjarne Stroustrup doesn't seem to show this way although it shows how to use std::function which let you accomplish essentially the same thing. std::function is nice if you want to store a callable for later but it adds a bit of overhead so it's generally not used in situations where the callable is called directly from inside the function.

This is what call_twice would look like if we used std::function:

1
2
3
4
5
6
7
#include <functional>

void call_twice(std::function<void()> f)
{
	f();
	f();
}

Note that with this change call_twice is no longer a template. It's just a regular function.

What does "[&x]" translate to?

It's a lambda capture. The & means that x is captured by reference.

[&x](){ std::cout << ++x << "\n"; } is a "lambda expression". It's just a shortcut for creating a functor that contains a reference to x.

It's essentially equivalent to something like this:
1
2
3
4
5
6
7
8
9
10
11
struct IncAndPrint
{
	int& iref;
	void operator()() const
	{
		std::cout << ++iref << "\n";
	}
};

int x = 10;
call_twice(IncAndPrint{x});


They are both sent in as pass by value

I'm not sure it makes sense to say that SortDescend<int> is passed by value because it's not a value. It's a type.

It's passed in at compile time just like the first template argument int.
Last edited on
I have now read the chapters on Function Objects and Lambdas in my Sam's book and your postings are a lot clearer now. By reading and now duplicating the sample programs it is second hand nature to use functors with many STL & algorithms. Besides some of your good points, it just comes across as something you just do now. I also understand your lambda call_twice sample.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//function pointer
#include <iostream>
using namespace std;
 
int mult(int a, int b) { return a * b; }
 
int main()
{
    int (*funcMult)(int, int);
 
    funcMult = mult;
 
    cout << "Product: " << funcMult(11, 5) << endl;
    
    //Without ever looking, I thought it might be done this way (since can have overloaded functions)
    //mult(int, int)* pMult = mult(int, int);	//NOT WORK!!!
 
    return 0;
}


I also knew that function pointers were around, since they were mentioned in VFT, but I never knew how to create one manually and I never could have imagined this is how you actually do it.


Function pointers and virtual functions are often difficult/impossible to inline because they might end up calling different functions.


There are some points such as the one above that I will have to take your word for it. I thought virtual functions made it so that it is clear which function is used, depending on what called it (derived or base class) and from what object. But then again I have seen only straight forward ones.


They are both sent in as pass by value

I'm not sure it makes sense to say that SortDescend<int> is passed by value because it's not a value. It's a type.

It's passed in at compile time just like the first template argument int.


Not too sure what to make of this then, because I guess it is a type just like vector<int> is a type. But at the same time nothing is done without passing something in or without an address or reference. So the request below to make a set "knows" where the functor SortDescend is and I suppose puts one together during compile time and ends up knowing the address and it surely does the sorting in descending order. It has to make a temporary copy of the SortDescend functor in memory to use it and then destroys it afterword.

How would you word that then, passing the type indirectly passes the "knowledge" of where and how to use SortDescend? Passing "SortDescend<int>" as the second parameter is all the knowledge it needs to use it properly.

 
set<int, SortDescend<int>> s2{ 2,5,1,3,2,1,4 };  //For set 
I thought virtual functions made it so that it is clear which function is used, depending on what called it (derived or base class) and from what object.

It depends on the situation, and how aggressively the compiler inlines, etc. but imagine you have a function with a parameter that is a reference (or pointer) to Base that calls a virtual function on the object that is passed in.
1
2
3
4
void foo(Base& obj)
{
	obj.f();
}
Exactly which function f (or perhaps I should say "implementation of f") that gets called depends on the object that is passed in since you can pass in objects of type Base or any other type that is derived from Base. If you pass in a Base it will call Base::f and if you pass a Derived it will call Derived::f. This essentially makes inlining of obj.f() impossible (although if foo is inlined and the dynamic type of the object is known to the compiler in that context it could still inline it).

I guess it is a type just like vector<int> is a type.

Yes.

It has to make a temporary copy of the SortDescend functor in memory to use it and then destroys it afterword.

It has to create a functor every time it needs to use it yes but any overhead of doing that is likely to get optimized away.
Last edited on
Your making me think beyond my just copying samples of code, which is stopping me cold but good, so thanks!

Honestly, what I read said that the VFT is created during compile time and that is EXACTLY how it knows which function to call during run time and that it leaves NO ambiguity. Because if Base was sent as pass by value, pointer, or reference, it will call base methods. If derived was passed as base ref or base pointer, then it calls derived overriding method. So what you are telling me is that the compiler doesn't like to look too deeply at the VFT when it is trying to inline? Or somehow inheritance and overriding methods can be more complicated that a VFT might not provide the correct method call and thus cannot be inlined? Disregard what the compiler does for one second with inlining, if you as a programmer just looked at the items to be inlined and the VFT during compile time, do you have all the info to figure out if it can be inlined yourself and its just that the compiler has the info but might not want to do it?


Let me recap types vs objects, so that I make sure I get it right:
1
2
set<int> s2{ 2,5,1,3,2,1,4 };  //For set
set<int, SortDescend<int>> s2{ 2,5,1,3,2,1,4 };  //For set 


You said we are sending "SortDescend<int>" as a type when creating the custom sort predicate. I do realize now that it is a type in this case, because we are creating a template and it needs the 2nd argument as a type and not a pass by value.
Let me analyze some more to get the wording right on other scenarios.

A) "set" is a class that is templatized.
You can't simply write "set s2{ 2,5,1,3,2,1,4 };" because it needs the type of template. So what can be said about "set<int>" in here? Is it fair to call it a type here or do we call it a class or the full realization that it is a templatized class that is a custom type?

B) MyClass classObj;
"MyClass" here is a class, but it can also be considered a type, because classes are custom types.

C)
(s2) This sends in an object of type "set<int>" or I guess we can say of class "set<int>".
SortDescend<int> mySortDescend1; (mySortDescend1) This sends in an object.
(SortDescend<int>) This sends in a type, which is REALLY a struct that is templatized for int type.
(SortDescend<int>()) This sends in a temp obj that then get destroyed.

D)vector<int>::iterator iter;
When I first saw this I thought the left of :: was the class, referring to a class without an object, and that it called method iterator that then somehow returned an object.
But now it seems that "vector<int>::iterator" is a type definition for class iterator, that then "iterator iter" creates the object iter.

E) This last one is a bit different.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include<vector>
#include<algorithm>
using namespace std;

template<typename T>
void Display(const T& c)
{
	for (auto iter = c.cbegin(); iter != c.cend(); ++iter)
		std::cout << *iter << std::endl;

}

int main()
{
        vector<char> v1{ 'A', 'b', 'C', 'd' };
	//transform(v1.begin(), v1.end(), v1.begin(), ::toupper);	//WORKS!
	//transform(v1.begin(), v1.end(), v1.begin(), toupper);	//WORKS!
	transform(v1.begin(), v1.end(), v1.begin(), std::toupper);	//WORKS!
	Display(v1);

	toupper;		//Works!
	::toupper;		//Works!
	std::toupper;	//Works!
	::cout;		//Works!
	cout << ::endl; //Works!

	return 0;
}

When I first read the "::toupper" from my book, my imagination was running wild to figure out how this was working and I simply used this all along without knowing how it really works. I never saw anything that did not have something on the left side of :: and as an argument nonetheless. I thought it was a function part of vector and somehow the transform() backfed the info via a bool or some integer that gets interpreted.

I finally looked into it this week and it looks like it is part of std and that it is a function that takes an int and returns an int for the char's that transform iterates through.
Also, it is probably a good idea to write "::toupper" as such, so that one doesn't mistake it as simply a variable and knows it is a call from a namespace.
'A' - yes you can. Consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <set>
#include <vector>
#include <iostream>

int main() {
	const std::set s {1, 2, 3, 4};
	const std::vector v { 'a', 'b', 'c'};

	for (const auto a : s)
		std::cout << a << ' ';

	std::cout << '\n';

	for (const auto a : v)
		std::cout << a << ' ';

	std::cout << '\n';
}


which displays


1 2 3 4
a b c


This came with C++17 as 'Class Template Argument Deduction'.
https://en.cppreference.com/w/cpp/language/class_template_argument_deduction

This is why IMO you should always learn from a book that covers the latest C++ standard (C++23 or as that isn't yet mainly implemented by compiler vendors then C++20).
Last edited on
Ah, OK.
I was getting errors without the template type definition and I now checked and mine was set to compile with C++14 in the settings.

Also, just to clarify I do understand that some inline of virtual functions will simply not work because of what is being done within the function, but I imagined that the VFT should point the compiler in the direction to figure that out.
Visual Studio 2022 detaults to C++14 as the language standard when starting a new C++ project/solution . You can change the default C++ language standard to a newer standard.

https://cplusplus.com/forum/lounge/271176/

Now when I start a new C++ project/solution my C++ default is /std:c++latest. That includes C++23 features that MS has crammed in so far.

If you are using C++ features from earlier standards you might receive notices a feature has been deprecated or removed if you use C++20 or Preview (C++Latest). Just set the project's language standard back to C++17 or C++14 and you are good to go.

Personally if I get that notice I upgrade the code to conform with the newer standard. I haven't received any notices for quite some time.

Buying a book that covers an older language standard before C++20 because it's cheaper is not a good idea. Even if a Sams book touts a C++ standard on the cover the book really doesn't cover that standard a lot. Just a couple of after thought examples at the end of the book. I learned that the hard way.

As seeplus says you would be better served buying and using a couple of books on C++20 and C++23. The Beginning C++20 or C++23 Apress books by would be worth the expense. Recommended to buy the eBook versions instead of the dead tree versions. The print edition of C++20 is rather flimsy.

If you already have the C++20 book (I seem to remember you mentioning you do) stop learning from the Sams book and crack open the C++20 book.

Both of the Apress books follow the same outline chapter by chapter. Chapter 10 is Function Templates.

I am currently reviewing the C++20 book from the beginning and am at chapter 17, class templates. Each time I go back over the material I find something new I forgot or didn't quite grasp earlier.

Learning from the C++20 book you will be thrown into the thick of using modules. There are a few subtleties when importing modules vs. including headers you will learn about after compile-time errors.

FYI both Apress books have an appendix on headers so you won't miss out.

Both books has GitHub repos of the source code, C++20 has many of the code examples with supplemental examples written to use headers instead of modules.

If you use Visual Studio you will have little to no problems with the module examples.
Pages: 123