I tried to define two template functions outputing pairs and vectors of any type. If it is overloaded operator it works fine, but for standard functions lookup fails to find function defined for pair if it is declared after called. Does anybody know why?
# include <bits/stdc++.h>
using namespace std;
int Hash(int x) {
return x;
}
template <typename T> int Hash(vector<T> x) {
int ans = 0;
for (auto c : x)
ans = 2 * ans + Hash(c);//Fails if c is a pair: no matching function for call to ‘Hash(std::pair<int, int>&)
return ans;
}
template <typename T, typename C> auto Hash(pair <T, C> x) -> int {
return Hash(x.first) + Hash(x.second) * 14;
}
template <typename T>
ostream &operator<<(ostream &os, vector <T> x)
{
os << "{";
int cou = 0;
for (auto c : x) {
if (cou++) os << ", ";
os << c;//Works even if c is a pair despite calling operator declared late
}
return os << "}";
}
template <typename T, typename C> ostream & operator<< (ostream &os, pair<T, C> x) {
return os << "<" << x.first << ", " << x.second << ">";
}
ostream & Output(ostream &os, const char * x) {
return os << x;
}
ostream & Output(ostream &os, int x) {
return os << x;
}
template <typename T>
ostream &Output(ostream &os, vector <T> x)
{
Output(os, "{");
int cou = 0;
for (auto c : x) {
if (cou++) Output(os, ", ");
Output(os, c);//Fails if c is a pair: no matching function for call to ‘Output(std::ostream&, std::pair<int, int>&)
}
return Output(os, "}");
}
template <typename T, typename C> ostream & Output (ostream &os, pair<T, C> x) {
return Output(Output(Output(Output(Output(os, "<"), x.first), ", "), x.second), ">");
}
int main() {
vector <pair <int, int> > x;
x.emplace_back(13, 10);
x.emplace_back(1, 16);
cout << x << endl;//Compiles and outputs {<13, 10>, <1, 16>}
cout << Hash(x) << endl;//no matching function for call to ‘Hash(std::pair<int, int>&) ans = 2 * ans + Hash(c);
Output(cout, x);//no matching function for call to ‘Output(std::ostream&, std::pair<int, int>&) Output(os, c);
}
I misunderstood what's happening
I suppose that
cout << x << endl;//Compiles and outputs {<13, 10>, <1, 16>}
compiles only due to the GCC bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56943
It looks you are right, clang doesn't compile this one
https://godbolt.org/g/1QQe5E
In any case, you should use forward declaration:
https://ideone.com/HKSCYx
I believe that the correct answer was already produced above: operators lookup rules should not differ much from standard lookup rules; you cannot call an operator if it's declared below the line of code which calls it. You should use forward declarations to enable this.
On a related note: I think your code can be significantly improved:
Output(x, y)
function at all—it only clutters the code; usex << y
directly. In particular,return Output(Output(Output(Output(Output(os, "<"), x.first), ", "), x.second), ">");
can be replaced withos << "<" << x.first << ", " << x.second << ">"
, which is much less verbose. It's C++, not Java, we know what<<
means in IO-context.operator<< (ostream &os, const pair<T, C> &x)
, notoperator<< (ostream &os, pair<T, C> x)
. Otherwise, the pair may be copied for output, which is useless. The same thing applies forvector<T>
andfor (auto c : x)
; usefor (const auto &c : x)
.ans = 2 * ans + Hash(c);
is a very bad polynomial hash. The base should be greater than number of distinct hashes of a single element at the very least, otherwise,{0, 2}
and{1, 0}
have the same hash.Hash(x.first) + Hash(x.second) * 14;
. Use a bigger constant.unsigned int
(or simplyunsigned
) orunsigned long long
.Ad 1. The whole point is that it is the same, but operator<< compiles, while Ourput doesn't Ad 2,3,4,5. That's not a real code i want to use, just a sample code to illustrate the problem with name visibility