<< operator overloading and template specialization - c++

As part of study purpose, I was just playing with template specialization together with operator overloading
#include <iostream>
template<class T>
void operator<<(std::ostream& COUT,T val)
{
std::operator<<(COUT,val); //works for basic data types
}
//specializing for const char*
template<>
void operator<<(std::ostream& COUT,const char* val)
{
std::operator<<(COUT,val);
}
int main()
{
std::cout<<"samplestring"; //getting error here
return 0;
}
I am getting following error which I cannot figure out.
<source>:17:14: error: ambiguous overload for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'const char [13]')
17 | std::cout<<"samplestring";
| ~~~~~~~~~^~~~~~~~~~~~~~~~
| | |
| | const char [13]
| std::ostream {aka std::basic_ostream<char>}
what is the problem here
Thank you..!

The problem is there is already operator<<(std::ostream& COUT,const char* val) defined by the standard library.
So now there are two symbols
std::operator<<(std::ostream& ostream,const char* val);
operator<<(std::ostream& COUT,const char* val); // YOURS
Because the symbols are different, there are no multiple-definition errors.
For a call std::cout << "Hello", the compiler searches for all operator<< functions currently defined at the call site. That looks like it is just your function, but the compiler also looks into all namespaces in which the passed arguments are defined. In this case it is std:: namespace and there the compiler finds the STL version. That is called argument dependent lookup(ADL).
Since both functions are templates and neither is more specialized than the other, the call is ambiguous.
Please do not use all capital letters as variable COUT, that is usually reserved for macros, with one-letter template parameters being exception T.
//works for basic data types
That is also not true, std::cout<<5; will NOT call your definition with T. Because there exists std::ostream::operator<<(int v) method. Same lookup happens but since this method is not a template, it is a better candidate and thus chosen. Your template function is never called.

Related

Template conversion operator issue

I have done a bit of browsing and this was the most relevant link that I could find, however it does not answer my question
Question: Why does the template substitution fail and the following does not compile?
template <typename T>
struct A
{
A() {};
A(T value) : val(value){}
operator T() { return this->val;}
T val;
};
A<std::string> test;
std::cout << "xxx" + std::string(test); //works fine
std::cout << "xxx" + test; //compiler error
Error message:
error: no match for 'operator+' (operand types are 'const char [4]' and 'A<std::__cxx11::basic_string<char> >')
19 | std::cout << "xxx" + test;
| ~~~~~ ^ ~~~~
| | |
| | A<std::__cxx11::basic_string<char> >
| const char [4]
std::operator+(std::basic_string) is a set of operator templates, template argument deduction needs to be performed on the 2nd operand test. But implicit conversion (from A<std::string> to std::string) won't be considered in template argument deduction.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
As you have showed, explicit conversion like "xxx" + std::string(test); works fine. You can also specify template arguments explicitly (in ugly way) to bypass template argument deduction.
operator+ <char, std::char_traits<char>, std::allocator<char>>("xxx", test);

Why can't the compiler use the std::string conversion function of the class when perform operator<<?

Consider the following struct with a user-defined conversion function that can convert itself to const char*;
struct S {
operator const char*() { return "hello"; }
};
This work with <iostream>, we can print the struct S with no error message:
std::cout << S{} << '\n';
But if I change the return type to std::string:
struct S {
operator std::string() { return "hello"; }
};
I got this compiler error message:
<source>:11:13: error: no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'S')
11 | std::cout << S{} << '\n';
| ~~~~~~~~~ ^~ ~~~
| | |
| | S
| std::ostream {aka std::basic_ostream<char>}
<source>:11:18: note: 'S' is not derived from 'const std::__cxx11::basic_string<_CharT, _Traits, _Allocator>'
11 | std::cout << S{} << '\n';
| ^
Why can't the compiler use the std::string conversion? Is there a difference between the conversion function of the built-in and class type?
Because operator<< for std::basic_string is a template taking 3 template parameters:
template <class CharT, class Traits, class Allocator>
std::basic_ostream<CharT, Traits>&
operator<<(std::basic_ostream<CharT, Traits>& os,
const std::basic_string<CharT, Traits, Allocator>& str);
And implicit conversion won't be considered in template argument deduction:
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
Then given std::cout << S{};, the template parameter CharT, Traits and Allocator can't be deduced on the 2nd function argument.
On the other hand, operator<< for const char* doesn't have such issue; given std::cout << S{};, the template parameter CharT and Traits would be deduced only from the 1st function argument. After deduction, the implicit conversion from S to const char* will be performed and the calling works well.

How do you make a fold expression with custom operator <<?

I am trying to make a print function based on parameter pack and fold expression. My implementation fails to compile on clang 10.0. Here is the code:
#include <iostream>
#include <set>
std::ostream &operator<<(std::ostream &strm, const std::set<int> &set) {
strm << set.size();
return strm;
}
template <typename... Args> void Print(std::ostream &strm, Args &&... args) {
(strm << ... << args);
}
int main(int, char **) {
std::set<int> my_set;
Print(std::cout, my_set);
}
and here is the compiler output
#1 with x86-64 clang 10.0.0
<source>:10:12: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
(strm << ... << args);
^
<source>:15:3: note: in instantiation of function template specialization 'Print<std::set<int, std::less<int>, std::allocator<int> > &>' requested here
Print(std::cout, my_set);
^
<source>:4:15: note: 'operator<<' should be declared prior to the call site
std::ostream &operator<<(std::ostream &strm, const std::set<int> &set) {
^
1 error generated.
Compiler returned: 1
The same code compiles with gcc 10.1. Is this a compiler bug (or missing feature), or is my implementation incorrect?
Argument-dependent lookup is not helping you here.
The error message is:
:10:12: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
So it's looking for an operator<< to call:
It looks in the definition of std::set - nope, none there.
It looks in namespace std (because that's where std::set lives) - nope, not there, either.
Should it look in the global namespace? I don't know.
Clearly gcc and clang have different opinions about that.

stuck --- Creating an Operator Template in C++

I am creating a class SpyOutput that mimics cout, and I am trying to use a template so I don't have to overload the << operator 4 times (one for each data type):
#include <iostream>
#include <sstream>
using namespace std;
class SpyOutput
{
ostream *os;
stringstream ss;
int sum, temp;
public:
SpyOutput(ostream *s):os(s), sum(0){}
template <class T>
SpyOutput& operator<<(T x)
{
ss << x;
*os << x;
return *this;
}
};
int main(void)
{
SpyOutput spy(&cout);
spy << "Hello" << endl;
spy << "1235" << endl;
spy << 'z' << endl;
spy << 4.56 << endl;
return 0;
}
I can't get it to compile, it doesn't seem to recognize my template. Any ideas? The G++ error message is
main.cpp: In function 'int main()':
main.cpp:24:20: error: no match for 'operator<<' (operand types are 'SpyOutput' and '<unresolved overloaded function type>')
spy << "Hello" << endl;
^
main.cpp:24:20: note: candidates are:
main.cpp:13:16: note: template<class T> SpyOutput& SpyOutput::operator<<(T)
SpyOutput& operator<<(T x)
^
main.cpp:13:16: note: template argument deduction/substitution failed:
main.cpp:24:23: note: couldn't deduce template parameter 'T'
spy << "Hello" << endl;
^
The key part of the error message is "template argument deduction/substitution failed". endl is not an object, it's not even a function. It is a function template, which is a "cookie cutter" used to generate an infinite number of cookie-functions. When << to a ostream, the the compiler sees if any of the potential << functions can clarify what the template arguments should be. And there exists this overload:
ostream& operator<< (ostream& (*pf)(ostream&));
When the compiler checks this overload, it realizes that it can unambiguously specialize endl as a ostream& (*)(ostream&), and so selects this overload and does the implicit specialization matching the stream's type. Luckily, all you have to do is also provide the above function overload, and hopefully this magic will work for you as well.
As a note, ostreams have two other important overloads as members you'll have to add as well:
ostream& operator<< (ios& (*pf)(ios&));
ostream& operator<< (ios_base& (*pf)(ios_base&));
It's also worth mentioning that your function is attempting to make copies of all objects you stream, which can cause it to fail or act wierd. A more sane idea is to use a universal reference. Or at least capture by const reference.
//if T is a template, then T&& is a universal reference. A perfect match for everything
//if T were not a template, it would be an rvalue reference. Totally unrelated.
template <class T> SpyOutput& operator<<(T&& x) {
ss << x;
*os << x;
return *this;
}
Your code fails simply because std::endl is a function template, and therefore the compiler needs to know which instantiation of the template you want to use. The standard IOStream classes have separate overloads for manipulators, and they explicitly specify the template instantiation. You can do that as well:
SpyOutput& operator<<(std::ostream& (*manip)(std::ostream&))
{
if (os) manip(*os);
return *this;
}
Now when you do spy << std::endl this will instantiate std::endl<char, std::char_traits<char>> which is valid, and the code will work. Though in general I wouldn't recreate an entire stream class but rather use the std::streambuf interface.

"ambiguous overload for 'operator[]'" if conversion operator to int exist

I'm trying to implement the vector like and the map like [] operator for a class. But I get error messages from my compilers (g++ and clang++). Found out that they only occurs if the class has also conversion operators to integer types.
Now I have two problems. The first is that I don't know why the compiler can't distinguish between [](const std::string&) and [](size_t) when the class has conversion operators to ints.
The second... I need the conversion and the index operator. How to fix that?
works:
#include <stdint.h>
#include <string>
struct Foo
{
Foo& operator[](const std::string &foo) {}
Foo& operator[](size_t index) {}
};
int main()
{
Foo f;
f["foo"];
f[2];
}
does not work:
#include <stdint.h>
#include <string>
struct Foo
{
operator uint32_t() {}
Foo& operator[](const std::string &foo) {}
Foo& operator[](size_t index) {}
};
int main()
{
Foo f;
f["foo"];
f[2];
}
compiler error:
main.cpp: In function 'int main()':
main.cpp:14:9: error: ambiguous overload for 'operator[]' in 'f["foo"]'
main.cpp:14:9: note: candidates are:
main.cpp:14:9: note: operator[](long int, const char*) <built-in>
main.cpp:7:7: note: Foo& Foo::operator[](const string&)
main.cpp:8:7: note: Foo& Foo::operator[](size_t) <near match>
main.cpp:8:7: note: no known conversion for argument 1 from 'const char [4]' to 'size_t {aka long unsigned int}'
The problem is that your class has a conversion operator to uint32_t, so the compiler does not know whether to:
Construct a std::string from the string literal and invoke your overload accepting an std::string;
Convert your Foo object into an uint32_t and use it as an index into the string literal.
While option 2 may sound confusing, consider that the following expression is legal in C++:
1["foo"];
This is because of how the built-in subscript operator is defined. Per Paragraph 8.3.4/6 of the C++11 Standard:
Except where it has been declared for a class (13.5.5), the subscript operator [] is interpreted in such
a way that E1[E2] is identical to *((E1)+(E2)). Because of the conversion rules that apply to +, if E1 is an
array and E2 an integer, then E1[E2] refers to the E2-th member of E1. Therefore, despite its asymmetric
appearance, subscripting is a commutative operation.
Therefore, the above expression 1["foo"] is equivalent to "foo"[1], which evaluates to o. To resolve the ambiguity, you can either make the conversion operator explicit (in C++11):
struct Foo
{
explicit operator uint32_t() { /* ... */ }
// ^^^^^^^^
};
Or you can leave that conversion operator as it is, and construct the std::string object explicitly:
f[std::string("foo")];
// ^^^^^^^^^^^^ ^
Alternatively, you can add a further overload of the subscript operator that accepts a const char*, which would be a better match than any of the above (since it requires no user-defined conversion):
struct Foo
{
operator uint32_t() { /* ... */ }
Foo& operator[](const std::string &foo) { /* ... */ }
Foo& operator[](size_t index) { /* ... */ }
Foo& operator[](const char* foo) { /* ... */ }
// ^^^^^^^^^^^
};
Also notice, that your functions have a non-void return type, but currently miss a return statement. This injects Undefined Behavior in your program.
The problem is that f["foo"] can be resolved as:
Convert "foo" to std::string (be it s) and do f[s] calling Foo::operator[](const std::string&).
Convert f to integer calling Foo::operator int() (be it i) and do i["foo"] using the well known fact that built-in [] operator is commutative.
Both have one custom type conversion, hence the ambiguity.
The easy solution is to add yet another overload:
Foo& operator[](const char *foo) {}
Now, calling f["foo"] will call the new overload without needing any custom type conversion, so the ambiguity is broken.
NOTE: The conversion from type char[4] (type type of "foo") into char* is considered trivial and doesn't count.
As noted in other answers, your problem is that [] commutes by default -- a[b] is the same as b[a] for char const*, and with your class being convertible to uint32_t this is as good a match as the char* being converted to std::string.
What I'm providing here is a way to make an "extremely attractive overload" for when you are having exactly this kind of problem, where an overload doesn't get called despite your belief that it should.
So here is a Foo with an "extremely attractive overload" for std::string:
struct Foo
{
operator uint32_t() {return 1;}
Foo& lookup_by_string(const std::string &foo) { return *this; }
Foo& operator[](size_t index) {return *this;}
template<
typename String,
typename=typename std::enable_if<
std::is_convertible< String, std::string >::value
>::type
> Foo& operator[]( String&& str ) {
return lookup_by_string( std::forward<String>(str) );
}
};
where we create a free standing "lookup by string" function, then write a template that captures any type that can be converted into a std::string.
Because it "hides" the user-defined conversion within the body of the template operator[], when checking for matching no user defined conversion occurs, so this is preferred to other operations that require user defined conversions (like uint32_t[char*]). In effect, this is a "more attractive" overload than any overload that doesn't match the arguments exactly.
This can lead to problems, if you have another overload that takes a const Bar&, and Bar has a conversion to std::string, the above overload may surprise you and capture the passed in Bar -- both rvalues and non-const variables match the above [] signature better than [const Bar&]!