#Generic Overloaded Template being used over Specific Template

48 messages · Page 1 of 1 (latest)

flint kayak
#

I'm getting a compile error for the following code, which tries to overload cout to print a hashmap<string, vector<string>>. Because map iterators give pairs, I created an overload for the pair class. However, when compiling, the pair object attempts to use the generic one over the specific one. I read that the compiler tries to find the most specific one...

#include <unordered_map>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

template<typename T , template<typename...>class C , typename... Z , typename = enable_if_t<!is_same_v<C<T , Z...> , string>>>
ostream& operator<<(ostream& os , const C<T , Z...>& c) { 
    os << '{';
    if (c.size()) {
        os << *c.begin();
        for (auto it = ++c.begin();it != c.end();++it)
            os << ", " << flush << *it; 
    }
    return os << '}'; 
}
template<typename T , typename U>ostream& operator<<(ostream& os , const pair<T , U>& p) { 
    return os << '(' << p.first << ", " << p.second << ')'; 
}

int main() {
    unordered_map<string , vector<string>> graph;
    graph["a"].push_back("b");
    graph["b"].push_back("a");
    cout << graph << endl;
}

Here is a snippet of my error:

   10 |     if (c.size()) {
      |         ~~^~~~
.\z.cpp:11:18: error: 'const struct std::pair<const std::__cxx11::basic_string<char>, std::vector<std::__cxx11::basic_string<char> > >' has no member named 'begin' 
   11 |         os << *c.begin();
      |                ~~^~~~~
green dustBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

dense heart
#

generally, creating overloads for types you don't control is considered a bad idea

#

there should (maybe?) be a listing of the templates it rejected

#

do you see that in the error?

flint kayak
#

I know creating all these overloads is a bad idea, but I'm using this in a competitive programming context, so safety and good practices don't really matter , just speed. I want to quick way to print any container I need for debugging purposes.

The error is quite small and only indicates pair<> is using the generic one over the specific one...

#

this is very perplexing given that every source says the compiler will always choose the least generic one, and the first one is clearly very unspecific, taking variadic parameters and such. The pair overload is so incredibly specific, but there's no way the compiler is wrong, right?

#

Also, I've already tried adding pair<T, Z...> into the enable_if_t statement, but it fails even harder, unable to match to the map object at the very beginning

dense heart
#

the rules for "specificity" can be a bit uhhhh complex.

#

here's something, have you tried putting your overload in namespace std

#

cause uhh ADL rules

#

which I think are involved in template specificity?

#

honestly this is a field of C++ arcana I avoid with one simple trick (when I can)

#

putting everything in a single namesapce Doggers

flint kayak
#

@dense heart
before I close the forum, it DID work, but do you think you could explain why? This makes absolutely no sense

dense heart
#

or a page it links

#

but you'll note that it is a very long page

#

which is an infamous aspect of c++ resolution

#

it basically comes back to "how does the code select the right operator"

#

right so if I do std::vector v = { 1, 2 }; begin(v); how does it know to call std::begin?

#

this is especially important for operators

#

how does my overload of operator+ get selected when I don't do using namespace my_namespace; for all my random namespaces

#

but I can still do my_namespace::AType a, b; a+b and expect my code to run

#

that is adl

#

adl is part of overload resolution, and candidate template selection is probably not at a fun time

#

so you got far enough to get a template error, but not far enough for the overload search to expand

#

or it's possible your template was never a candidate

#

like even cppref seems vague

#

all declarations found by unqualified name lookup of operator@ in the context of the expression (which may involve ADL), except that member function declarations are ignored and do not prevent the lookup from continuing into the next enclosing scope.

flint kayak
#

my knowledge is limited to college c++ where they got single-level templating, but I've just been figuring out how templates can be nested and the rules seemed about right

I can't really find mentions of namespaces, but it might be a slight nuance after using using namespace std

#

looks rough, but thanks for the help

green dustBOT
#

Thank you and let us know if you have any more questions!

This thread is now set to auto-hide after an hour of inactivity

dense heart
flint kayak
#

I knew c++ was dangerously flexible, so I figured I'd try to get a universal print-like function for the sake of it, but instead spent several hours of the day trying to re-lean templates and some more

dense heart
#

anyway the key insight I can offer is that for templated operator overloads (and free/ADL functions) is that if you want the template and/or overload to be considered properly you probably need to put it in the same namespace as the type

olive aurora
#

if you want it to print one of your custom types it just requires something like this:

// some custom type
struct Point {
    float x{};
    float y{};
};

constexpr auto format_as(const Point& pt)
{
    return fmt::format("(x={}, y={})", pt.x, pt.y);
}
flint kayak
#

Unfortunately because it’s comp programming the only header available is bits/c++17, so everything else has to be defined in file when I submit it

olive aurora
#

if it finds a format_as() function for any custom type, it'll use that in calls like this elsewhere in the code:

Point pt{ .x=1.0f, .y=0.1f };
fmt::print("{}", pt);
#

ah, bummer

#

fmt makes this type of thing really easy

#

std::cout with combined with folds and some basic constraints on the template functions you define unfortunately would also probably simplify the problem, but they're only in >= C++20

#

seems to cover most of the STL containers with explicit overloads