I see some complicated discussion on this topic but I'm failing to make what should be a very simple piece of code work. I've found two ways to make it work but I'm puzzled by the some issues of syntax. I just want to find the minimum value in a list. This works:
using namespace std;
#include <iostream>
#include <list>
#include <algorithm>
int main(void)
{
auto init_list = {10, 20, 30};
list<int> alist(init_list);
list<int>::iterator minnum = min_element(begin(alist), end(alist));
cout << "The min in the list is: " << *minnum << endl;
return(1);
}
But there is also the definition of min() which takes an initializer_list as an argument. I think I am misunderstanding the syntax of it. Since I've already defined an init_list I can call
int minint = min(init_list);
and that works. But I don't understand why. The documentation on min says this call should be of form:
template< class T, class Compare >
T min( std::initializer_list<T> ilist, Compare comp );
So shouldn't I have to specify a < comparison operator to be able to use this (apparently not...why?)? However, I don't know how to pass this operator correctly, and have been unable to come up with a clear explanation. Each of the following generate a wide variety of syntax errors:
int minint = min(init_list, operator<);
int minint = min(init_list, int::operator<);
How am I supposed to pass the operator? Clearly it doesn't matter in this case but if my list was not a list of ints but a list of some custom type with overloaded operators then would I need to pass an operator< ? Or does the syntax simply mean that class T must have a comparison operator defined, but you never have to specify it in the min function call?
There is version of min (third declaration) which takes only initialization list.
And if you want to be explicit about comparing with < relation, use std::less:
int minint = min(init_list, std::less<int>());
Thanks Tomasz Klak. I'm pretty new to operator overloading so I didn't know the std::less syntax. That's very useful to know. Clearly I wasn't reading carefully enough, since I missed that other definition of min(). My other question was about how this works with custom objects. I think I've answered that for myself now. Here's what I've found.
You can make an initialization list of custom objects. For example, I have a very simple class that I call Double_list_struct, which is just a container for an int (the index) and a double (the value), with some typical member functions (constructors, getters, setters, toString() and overloaded operators, including operator<). So I can make an initialization list of it by:
Double_list_struct doub1(1,5.5);
Double_list_struct doub2(1,3.5);
Double_list_struct doub3(1,26.5);
auto init_list_doub = {doub1, doub2, doub3};
Having done this the min() function works on it perfectly well.
Double_list_struct mindoub = min(init_list_doub);
I assume (but haven't checked) that if the Double_list_struct class hadn't included an operator< function I would have had to provide one.
Cheers!
Related
I am trying to create a set of function pointers in c++ but getting error while inserting / deleting elements from it.
#include<bits/stdc++.h>
using namespace std;
void func(int x) {
cout<<x;
}
int main() {
set <function<void (int)>> mine;
mine.insert(func);
return 0;
}
i am getting error
/usr/include/c++/6/bits/stl_function.h:386:20: error: no match for ‘operator<’ (operand types are ‘const std::function’ and ‘const std::function’).
I think that this problem is because of operator that will be used to compare set values , can someone suggest how to make this work ?
How can i write a comparator for function pointers in this case ?
If you want to store just function pointers, you don't need std::function:
using MyFunctionPointer = void(*)(int);
void func(int x);
std::set<MyFunctionPointer> orderedSet;
int main()
{
orderedSet.emplace(func);
}
Demo
This works because you can compare (for std::set) or hash (for std::unordered set) function pointer values. But comparing or hashing std::function instances is not implemented in the standard library, and there is no portable way to add that after the fact.
Edit: As pointed out by #HolyBlackCat, while the builtin operator< is not required to induce the required total order on function pointers, std::less (as used by std::set) is required to do so for any pointer.
I'm trying to find a good solution for the following problem:
I want to implement a function that takes a variable number of container arguments and returns the size of the biggest container. Here is an example:
std::vector<std::string> vStr(2, "foo");
std::vector<int> vInt(1, 123);
std::vector<double> vDouble(3, 1.1);
std::list<char> lChar(4, '*');
// or even more container
size_t uiMaxSize = getMaxContainerSize(vStr, vInt, vDouble, lChar /*, ...*/);
in this case getMaxContainerSize should return 4, because lChar has the biggest size of 4.
I've already implemented this workaround using cstdarg:
#include <cstdarg>
...
size_t getMaxContainerSize(int iCnt, ... )
{
size_t uiMaxSize = 0;
va_list ap;
va_start(ap, iCnt);
for(int i=0; i<iCnt; i++)
{
size_t uiTempSize = va_arg(ap, size_t);
uiMaxSize = uiMaxSize<uiTempSize ? uiTempSize : uiMaxSize;
}
va_end(ap);
return uiMaxSize;
}
...
size_t uiMaxSize = getMaxContainerSize( 4, vStr.size(), vInt.size(), vDouble.size(), lChar.size());
But with this I have to type .size() for every container and I also have to specify the number of containers. I also don't like to use C stuff in C++ programs and I'm asking myself if there is a better way to implement this. Maybe by using some class and overloading operator<<() so I can type something like this:
MaxSizeFinder cFinder;
cFinder << vStr << vInt << vDouble << lChar;
size_t uiMaxSize = cFinder.getResult();
Do you think something like this is possible? Any suggestions?
Thank you.
Use a variadic template:
template<typename... Conts>
std::ptrdiff_t getMaxContainerSize(const Conts&... conts) {
return std::max({conts.size()...});
}
When you pass containers as arguments, the compiler will deduce a list of types for Conts. Each parameter of the function will be a const <deduced type> &*. Using conts.size()... expands to conts1.size(), conts2.size(), ..., contsN.size(), where conts# is each argument given to the function. It turns out std::max has a handy overload that you can delegate this to.
There are a couple key advantages of variadic templates over C variadic functions:
They are type safe - the compiler is guaranteed to complain when types don't match, and you don't need a format string or anything.
The function knows how many arguments were passed, and you can get it with sizeof...(Conts).
Nothing special happens to the arguments when going in. In a variadic function, char would be an int by the time the function has to pick it out, among others.
You don't need to explicitly specify any of the types when you use the arguments. This means you can accept an infinite number of types instead of a predefined list (think printf's format specifiers).
Finally, per the comments, the return type was changed to a signed type that mostly acts as the signed counterpart to size_t (sort of like the non-standard ssize_t).
To future-proof the answer, there will soon be a std::size for a more generic way to get a container's size:
using std::size;
return std::max({size(conts)...});
This expands similar to above: size(conts1), size(conts2), ..., size(contsN)
*Normally, parameter packs are used with T&&... with std::forward instead of const T&.... This would potentially buy you something with third-party classes that have a more efficient size function when the object used is an rvalue. However, it adds complexity in general for a low chance at any benefit.
I was curious about why this piece of code doesn't work:
#include "stdafx.h"
#include <iostream>
#include <tuple>
#include <string>
#include <vector>
#include <algorithm>
typedef std::tuple<int, std::string> intString;
bool operator<(intString& lhs, intString& rhs){
return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs));
}
void printIntStrings(std::vector<intString>& v){
for (intString& i : v){
std::cout << std::get<0>(i) << " is " << std::get<1>(i) << std::endl;
}
}
int main(int argc, char* argv[])
{
std::vector<intString> v;
v.push_back(std::make_tuple(5, "five"));
v.push_back(std::make_tuple(2, "two"));
v.push_back(std::make_tuple(9, "nine"));
printIntStrings(v);
std::sort(v.begin(), v.end());
printIntStrings(v);
return 0;
}
As far as I can understand, I simply create a vector of intStrings and my operator should sort by the second element in the tuple first thus the output should be (last 3 lines anyway)
5 five
9 nine
2 two
However running it on my machine I get
2 two
5 five
9 nine
which implies that the sort is using the default less than operator, ignoring the one I specified. Note, adding const before the parameters didn't seem to affect anything.
I found three ways to "fix" this.
Fix #1
surround bool operator< ... in namespace std like so:
namespace std{
bool operator<(intString& lhs, intString& rhs){
return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs));
}
}
However I was told we should never add things to the std namespace since that behavior is undefined, so this fix seems the worst.
Fix #2
Add in something custom to the tuple like so:
enum class TRASH{DOESNTMATTER};
typedef std::tuple<int, std::string, TRASH> intString;
bool operator<(intString& lhs, intString& rhs){
return std::tie(std::get<1>(lhs), std::get<0>(lhs)) < std::tie(std::get<1>(rhs), std::get<0>(rhs));
}
(and obviously add in TRASH::DOESNTMATTER as the third make_tuple argument)
However, this seemed like a lot of work for something this simple. Also, it seems wasteful since the enum is not meaningfully used.
Fix #3
Use the predicate sort like so:
std::sort(v.begin(), v.end(), operator<);
This seemed to be the most elegant solution. However, I don't see why I have to explicitly tell the compiler to use my defined operator<.
So I want to know:
1) why this happens? Shouldn't c++ find my implementation and use that?
2) which "fix" is the best? if none of the ones I found, what would you recommend?
Any ideas? Thanks for reading!
Your operator< overload is not visible at the point where < is used (which is in the body of std::sort and/or any helper functions called by it, somewhere in <algorithm>).
If it is to be used, it must be picked up by argument-dependent lookup; but there's nothing in std::tuple<int, std::string> that has the global namespace as an associated namespace, so ADL doesn't help you, either, and the standard one is used.
Pass it as a comparator, preferably using a lambda or function object (which inlines better than function pointers), is the simplest fix. I'd also recommend renaming it; having a operator< overload with completely different semantics than the standard one, which may or may not be used by the expression a < b depending on where that expression is, is not a good idea.
you already fix it by your self
the problem is your operator< function doesn't override the default tuple::operator<, they are in different namespace
so, both your Fix#1 and Fix#3 are good solution
Fix#1 put them into the same namespace make it override correct,I think is the best way
What I am trying to do is:
#include <QVector>
#include <QLinkedList>
#include <QSet>
class MyType
{
//...
};
int main(int argc, char** argv)
{
QVector<MyType> vector;
QSet<QVector<MyType>::iterator> a;
a.insert(vector.begin()); // This is fine
QLinkedList<MyType> linkedList;
QSet<QLinkedList<MyType>::iterator> b;
b.insert(linkedList.begin()); // This does not compile
return 0;
}
The compiler message is:
error: no matching function for call to 'qHash(const QLinkedList<MyType>::iterator&)'
I know, that the reason why the first three lines compile is that for the QVector, the iterator is defined as typedef T* iterator; but for the QLinkedList it is a custom type.
I found out, that the QSet template class is implemented in terms of a hash table.
Apparently it is possible to evaluate the hash function for a pointer, but not for a custom type.
Please, could you tell me, how to overload the qHash function for my program to compile? I have read some basic information on the workings of a hash table, but I lack the confidence in the subject.
I tried to understand the inner workings of the QLinkedList<T>::iterator. It seems that it is very similar to the QVector<T>::iterator. It just holds a pointer to a node in the linked list instead of a pointer to the item itself.
class iterator
{
public:
...
Node *i;
...
};
So I tried to define the function in this manner:
uint qHash(QLinkedList<MyType>::iterator it)
{
return qHash(it.i);
}
The program compiled, but I have no confidence in my solution. How should I correctly overload the qHash function?
You did perfectly well already. The basic rules of hash functions are:
If x = y then hash(x) = hash(y).
If x != y then hash(x) != hash(y) (as often as possible). This isn't a strict rule, but the better it's followed, the better the performance of the hash table. Ideally, the outputs will appear random.
Your way works because if two iterators, ia and ib are equal (referring to the same node), their internal pointers, ia.i and ib.i will be equal. That takes care of rule 1. Then you use a built-in hash function on those pointers; Qt takes care of rule 2 for you.
Cheers!
I want to sort a vector using std::sort, but my sort method is a static method of a class, and I want to call std::sort outside it, but it seems to be trouble doing it this way.
On the class:
static int CompareIt(void *sol1, void *sol2) { ... }
std::sort call:
sort(distanceList.at(q).begin(),
distanceList.at(q).end(),
&DistanceNodeComparator::CompareIt);
Shouldn't it be possible to do this way?
std::sort takes a comparator that accepts value of the type held in the collection and returns bool. It should generally implement some notion of <. E.g., assuming your distanceList elements have collections of integers (I assume they don't, but for the sake of the example):
static bool CompareIt(int sol1, int sol2) { ... }
And of course you only need to supply a comparator if there isn't already a < operator that does the right thing for your scenario.
It should be a boolean method (sort uses operator <() by default to compare values)
The comparison function you've provided has the signature of the one needed by qsort, which is the sorting function that C provided before C++ came along. sort requires a completely different function.
For example if your declaration of distanceList is std::vector<DistanceNode> your function would look like:
static bool CompareIt(const DistanceNode &sol1, const DistanceNode &sol2)
{
return sol1.key < sol2.key;
}
Notice that sorting a std::list with the standard sort algorithm isn't efficient, which is why list supplies its own sort member function.
As others have mentioned, it needs a boolean return type. Here's an example which works:
#include "stdafx.h"
#include <vector>
#include <algorithm>
using namespace std;
class MyClass
{
public:
static bool CompareIt(const void *a1, const void *a2)
{
return a1 < a2;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
// Create a vector that contains elements of type MyData
vector<void*> myvector;
// Add data to the vector
myvector.push_back((void*)0x00000005);
myvector.push_back((void*)0x00000001);
// Sort the vector
std::sort(myvector.begin(), myvector.end(), MyClass::CompareIt);
// Display some results
for( int i = 0; i < myvector.size(); i++ )
{
printf("%d = 0x%08X\n", i, myvector[i] );
}
return 0;
}
[Edit] Updated the code above to make it a little simpler. I'm not suggesting it's nice code, but without know more about the OPs real implementation, it's difficult to give a better example!
First, the return type should be bool. Actually the requirement is only that the return type be assignable to bool, which int is. But the fact that you're returning int suggests that you might have written a three-way comparator instead of the strict weak ordering required by std::sort.
Your CompareIt function takes two void* pointers as parameters. Is distanceList.at(q) a vector<void*> (or vector of something convertible to void*)? If not, then the comparator inputs aren't right either. Using void* with algorithms also suggests that you're doing something wrong, because much of the point of generic programming is that you don't need opaque pointers that later get cast back to their original type.