Strange operator overloading behavior? - c++

#include <iostream>
using namespace std;
class Foo{
string _s;
public:
Foo(string ss){
_s = ss;
}
Foo& operator=(bool b){
cout << "bool" << endl;
return *this;
}
Foo& operator=(const string& ss){
cout << "another one" << endl;
return *this;
}
};
int main(){
Foo f("bar");
f = "this";
return 0;
}
I have overloaded = operator. I expected f = "this"; statement to call operator=(const string& ss) overload. But it does not. It calls operator=(bool b) overload. Why?

This operator operator=(const string& ss) requires a conversion of a user defined type for the argument (const char* to std::string), whereas the bool version has none and so provides a better match: you get the conversions from built-in types const char[5] to const char* to bool.

As noted by another answer, the pointer-to-char to bool conversion is preferred because it involves no user-defined conversions, while the std::string conversion has a user-defined conversion in it.
C++11 offers the ability to do manual type control.
template<size_t n>
struct enumarated_enum {
private:
enum empty {};
};
template<bool b, size_t n=0>
using EnableIf = typename std::enable_if< b, typename enumerated_enum<n>::empty >::type;
template<typename String, EnableIf< std::is_convertible< String, std::string >::value >... >
Foo& operator=(String&& s) {
cout << "string overload" << "\n";
}
// either this:
template<typename Bool, EnableIf< !std::is_convertible< Bool, std::string >::value && std::is_convertible< Bool, bool >::value, 1 >... >
Foo& operator=(Bool&& b) {
cout << "bool overload" << "\n";
}
// or this:
Foo& operator=(bool b) {
cout << "bool overload" << "\n";
}
where we match a type perfectly if it can be converted to a std::string, and the template doesn't match if it cannot be converted to std::string and other overloads are looked at.
If you have many types you want to be able to support, you have to use long logical forms that describe which of the overloads you want to run, and make sure the others don't accept it (with the not construct above). If you only have two types, the second type can be a traditional overload. The template overload gets first crack at anything that doesn't match the traditional overload exactly...
(The second template argument is a variardic list of numbered enums which cannot exist, whose only purpose is to make sure that the two templates differ in sufficient ways for the compiler not to complain.)

Without delving into C++ standards, on the surface your problem is, you defined your assignment overload with string& and a bool but in your test you're assigning a char* array.
So if you define a string and assign, it will work as expected.
Check it working here : http://codepad.org/owb6noXR

As others have said, an easy fix to this is to simply cast your string to an std::string, when doing the operator, so C++ knows exactly which overload to choose:
#include <iostream>
using namespace std;
class Foo{
string _s;
public:
Foo(string ss){
_s = ss;
}
Foo& operator=(bool b){
cout << "bool" << endl;
return *this;
}
Foo& operator=(const string& ss){
cout << "another one" << endl;
return *this;
}
};
int main(){
Foo f((string)"bar");
f = (string)"this";
return 0;
}

Related

Function call ambiguity in templatized code

Here is a (very distilled) use case and code example (sorry if it doesn't look too minimal, I couldn't figure out what else to exclude. The complete 'just compile' code can be found here: https://gcc.godbolt.org/z/5GMEGKG7T)
I want to have a "special" output stream, which, for certain user types, does special treatment during stream processing.
This special stream should be able to stream a "special" type with "special" treatment, as well as any other type which could be streamed through conventional streams - in the latter case, we simply use the conventional stream directly
struct Type { }; // Special type
struct Stream { // Special stream
// In this distilled example, we could achieve the same result
// by overriding << for ostream and Type, but the actual use case is wider
// and requires usage of the special stream type
// For illustration purposes only, we use this overload to cout "T!" for Types
Stream& stream(Type t) { std::cout << "T!"; return *this;}
// For any type for which cout << T{} is a valid operation, use cout for streaming
template<class T>
auto stream(const T& t) -> decltype(std::cout << std::declval<T>(), std::declval<Stream&>())
{
std::cout << t; return *this;
}
};
// Consumes every T for which Stream.stream is defined - those would be Type
// as well as anything else which can be sent to cout
template<class T>
auto operator<<(Stream& s, const T& t) -> decltype(s.stream(t))
{
s.stream(t);
return s;
}
This code works as expected:
void foo()
{
Stream stream;
stream << 10 << Type{};
}
Now I want to define a different struct, which has a member of Type in it, and a streaming operator for the same struct:
struct Compound {
Type t;
int i;
};
// We want the `<<` operator of the struct templated, because we want it to work with
// any possible stream-like object out there
template<class S>
S& operator<<(S& s, Compound comp)
{
s << comp.t << " " << comp.i;
return s;
};
Unfortunately, this doesn't work well. An attempt to use this << operator:
void bar(Compound comp)
{
Stream s;
s << comp;
}
Triggers an ambiguity:
error: ambiguous overload for 'operator<<' (operand
types are 'Stream' and 'Compound')
note: candidate: 'decltype (s.Stream::stream(t)) operator<<(Stream&, const T&)
note: candidate: 'S& operator<<(S&, Compound) [with S = Stream]'
That makes sense, since after defining templated operator for << for Compound, it could be streamed via cout, as well as through this new operator!
I can certainly work aorund this by making << for Compound non-templated, and instead use the special Stream as a stream argument - but I'd like to avoid it.
Is there a way I can preserve the templated << for Compound and still avoid the ambiguity?
So you need to lower the priority of operator<<(Stream& s, const T& t) to make it the last resort.
You can do it by making it (seemingly) less specialized, by templating the first parameter:
auto operator<<(std::same_as<Stream> auto &s, const auto &t) -> decltype(s.stream(t))
{
s.stream(t);
return s;
}
The change I made to the second parameter is unimportant, it's just for terseness.
Some extra boilerplate maybe?
template<class T> struct IsDefault: std::true_type {};
template<class T> concept Default = IsDefault<T>::value;
struct Stream {
template<Default T> Stream &operator<<(T &&t) {
return std::cout << t, *this;
}
};
struct Compound {};
template<> struct IsDefault<Compound>: std::false_type {};
Stream &operator<<(Stream &s, Compound &&c) { return s << "Compound"; }

ADL does not find templated free function

I tried to understand how ADL works, at least its basics, and created following piece of code:
#include <iostream>
#include <string>
#include <vector>
using std::pair;
using std::string;
using std::vector;
namespace My {
using std::to_string;
/** With these forward declarations it works ...
string to_string(char arg);
const string& to_string(const string& str);
template <typename T>
string to_string(const vector<T>& rhs);
template <typename T1, typename T2>
string to_string(const pair<T1, T2>& rhs);
struct A;
string to_string(const A& rhs);
*/
string to_string(char arg)
{
return string(1, arg);
}
const string& to_string(const string& str)
{
return str;
}
template <typename T>
string to_string(const vector<T>& rhs)
{
string str("");
for (const auto& e : rhs) {
str += to_string(e) + " "; //< this fails with `pair<..>`
/// `(to_string)(e)` would fail even with `A`
}
return str;
}
template <typename T1, typename T2>
string to_string(const pair<T1, T2>& rhs)
{
return to_string(rhs.first) + " " + to_string(rhs.second);
}
struct A {
static int counter;
string to_string() const
{
using My::to_string; //< avoid using `A::to_string`
return to_string("<A>[") + to_string(counter++) + to_string("]");
}
};
int A::counter = 0;
string to_string(const A& rhs)
{
return rhs.to_string();
}
}
int main(int /*argc*/, const char* /*argv*/[])
{
using My::to_string;
using My::A;
using std::cout;
using std::endl;
cout << to_string(3.1415) << endl;
cout << to_string(pair<int, char>{5, 'a'}) << endl;
cout << to_string(pair<double, pair<int, int>>{3.14, {1, 2}}) << endl;
cout << to_string(vector<int>{1, 2, 3}) << endl;
cout << to_string(pair<string, vector<int>>{"key", {1, 2, 3}}) << endl;
cout << to_string(pair<string, A>{"key", {}}) << endl;
cout << to_string(vector<A>{{}, {}, {}}) << endl;
/// this will fail to compile
cout << to_string(vector<pair<string, int>>{{"a", 1}, {"b", 2}}) << endl;
return 0;
}
I figured out that, inside of My, using std::to_string will use std:: free function if it exists and will use My:: otherwise. Then, outside of namespace My, it is sufficient to do using My::to_string to cover both cases. Fine so far.
Then I used the same technique inside member function A::to_string to avoid preferring the function itself instead of free functions (the same will apply for any other member function).
Finally, I was a little surprised that to_string(vector<A>) compiles, although A is not forward-declared. That's where ADL kicks in, as I've understood. Disabling it (enclosing to_string into brackets) will cause compilation failure.
Now after the long story, here goes my question: why ADL does not work in this case for templated function, i.e. to_string(pair<T1, T2>)? And more importantly, how to fix it? I would be glad if it was not necessary to perform forward declarations, as in my use case the definition of to_string(vector<T>) is located in some base header file and the latter definitions are in different headers and are not supposed to be known at this time.
EDIT:
I tried to somehow "fake" needed forward declarations by some templates or even with some SFINAE, but it either led to ambiguities or to the same result. Finally, I came up with solution which uses member function to_string (but it could be any other name) of desired class. This requires to always implement this function if compatibility with to_string is desired, which in case of STL containers requires to inherit them and add the member function. But I believe that in this case ADL will never fail.
Here are modified parts of the code:
template <typename T,
typename Fun = decltype(&T::to_string),
typename = std::enable_if_t<
std::is_member_function_pointer<Fun>::value>>
string to_string(const T& rhs)
{
return rhs.to_string();
}
template <typename T>
struct Vector : public vector<T> {
using vector<T>::vector;
string to_string() const
{
using My::to_string; //< avoid using `Vector::to_string`
string str("");
for (const auto& e : *this) {
str += to_string(e) + " ";
}
return str;
}
};
template <typename T1, typename T2>
struct Pair : public pair<T1, T2> {
using pair<T1, T2>::pair;
string to_string() const
{
using My::to_string; //< avoid using `Pair::to_string`
return to_string(this->first) + " " + to_string(this->second);
}
};
However, one has to replace vectors and pairs with Vectors and Pairs. (Free function to_string(A) is not needed any more).
Other solutions, comments?
why ADL does not work in this case for templated function, i.e. to_string(pair<T1, T2>)?
ADL works by inspecting the namespaces associated with the types involved in a given call. It then will consider the appropriate overloads found in those namespaces and select the best one.
to_string(vector<pair<string, int>>{{"a", 1}, {"b", 2}})
This call will select the overload My::to_string(const vector<T>&) and this in turns calls to_string(std::pair<std::string, int>).
ADL inspect then the namespaces associated with std::pair, std::string and int to look for the overload to_string(std::pair<...>). Since no such overload is defined in the namespace std it needs to find the definition previous to the call, but the overload My::to_string(const pair<T1, T2>&) is defined after. That's why you need to forward declare it.
Notice that you need to forward declare it because you also have this:
to_string(pair<string, vector<int>>)
If, on the other hand, you had something like:
to_string(vector<pair<string, My::A>>{{"a", {}}, {"b", {}}})
Then, one of the associated namespaces for the call will be My itself and the overload to_string(std::pair<...>) will be found without the need to forward declare it.

what is the right way to define typecast operator template in C++

this code here is my attempt to define a template to typecast or assignment operator. Problem is, this is not calling typecast function template at line 27 while typecasting the object.
#include <iostream>
#include <string>
using namespace std;
struct FFJSON{
template <typename T>
FFJSON& operator=(T const& t){
cout << "Assigning the object" << endl;
ts=(void*)&t;
return *this;
};
FFJSON& operator=(const char* s){return *this;};
FFJSON& operator=(const string& s){return *this;};
FFJSON& operator=(const int& i){return *this;};
FFJSON& operator=(const unsigned int& i){return *this;};
template <typename T>
operator T(){
cout << "Returning the object." << endl;
return *ts;
}
void* ts;
};
int main(int argh, char** argv) {
FFJSON f;
timespec tt = {3,2};
f = tt;
timespec& t=(timespec&)f;
cout << "sec:"<<t.tv_sec<<endl;
cout << "usec:"<<t.tv_nsec<<endl;
return 0;
}
Actual output:
Assigning the object
sec:126885484802960
nsec:4197094
Expected output:
Assigning the object
Returning the object
sec:3
nsec:2
The similar scenario is giving typecast operator not defined compile time error in an another program.
template <typename T> operator T() is fine, but such T can only be deduced to a non-reference type so this operator will not be used to convert to the type timespec&. Instead, your cast is a reinterpret_cast, which never performs a user-defined conversion.
In order to write a conversion operator that can produce an lvalue, you would need to write template <typename T> operator T&().
BTW, you can't dereference a void pointer. If your operator template were ever instantiated, you would get a compilation error because of that.
timespec& t=(timespec&)f; cannot use the typecast operator because you are casting to timespec& and not to timespec.
You have to implement
operator T&(){
cout << "Returning the object." << endl;
return *reinterpret_cast<T*>(ts);
}
to make this work.
Please do not use C-style casts in C++ modules. Use static_cast<>, dynamic_cast<>, reinterpret_cast<> and - if you really have to - const_cast<> instead.
I am not sure what you are trying to achieve, but the following implementation of the assignment operator copies the source value and should work for temporary objects and the typecast operator checks if the conversion may be valid.
class FFJSON
{
public:
template <typename T>
inline FFJSON& operator=(T const& t){
cout << "Assigning the object" << endl;
assert(std::is_trivially_copyable<T>::value );
data.resize(sizeof(t));
*reinterpret_cast<T*>(data.data()) = t;
return *this;
};
template <typename T>
inline operator T(){
cout << "Returning the object." << endl;
assert(sizeof(T) <= data.size());
return *reinterpret_cast<T*>(data.data());
}
private:
std::vector<char> data;
};

How to write a `<<` operator for boost::tuple?

In the sample code below, it shows that boost::tuple can be created implicitly from the first template argument.
Because of that I am not able to write a << operator as it becomes ambiguous.
Also I don't understand why ostringstream& << float is also ambiguous. This does not have any implicit construction. Why does this also give ambiguous error?
#include <iostream>
#include <boost/tuple/tuple.hpp>
#include <sstream>
#include <string>
using namespace std;
class Myclass
{
};
typedef boost::tuple<int,float,Myclass> Mytuple;
ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)
{
float f = tuple_.get<1>();
//os_ << (int)tuple_.get<0>(); // Error because int is implicitly converted into Mytuple. WHYY?
//os_ << tuple_.get<1>(); // No Clue Why this is ambiguous.
//os_ << tuple_.get<2>(); // Error because no matching operator. Fine.
return os_;
}
int main()
{
Mytuple t1;
t1 = 3; // Working because int is implicitly converted into Mytuple!! WHY?
//t1 = 3.0f; // Error because no matching constructor. Fine.
return 0;
}
Error Mesasge:
tupleTest2.C:18: error: ISO C++ says that these are ambiguous, even
though the worst conversion for the first is better than the worst
conversion for the second:
The problem is not with the tuple, but with your operator. This works fine :
ostream& operator<<(ostream& os_, Mytuple tuple_)
{
os_ << tuple_.get<0>(); // Error because int is implicitly converted into Mytuple. WHYY?
os_ << tuple_.get<1>(); // No Clue Why this is ambiguous.
//os_ << tuple_.get<2>(); // Error because no matching operator. Fine.
return os_;
}
The problem is that the ostringstream inherit operator<< from ostream, which has this signature : ostringstream& operator<<(ostringstream& os_, Mytuple tuple_) is allowed. Then the
ostream& operator<<(ostream& os, T t)
(change T with all available types in c++, see operator<< reference page
EDIT
Here is a simplified example (without a tuple) :
ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)
{
const int i = tuple_.get<0>();
os_ << i; // error in this line
return os_;
}
and the error is now :
dfg.cpp: In function ‘std::ostringstream& operator<<(std::ostringstream&, Mytuple)’:
dfg.cpp:18: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/ostream.tcc:111: note: candidate 1: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(int) [with _CharT = char, _Traits = std::char_traits<char>]
dfg.cpp:14: note: candidate 2: std::ostringstream& operator<<(std::ostringstream&, Mytuple)
The above error message says : it is not possible to choose between two operators operator<<(ostream&,...) and operator<<(ostringstream&,...). This also raises another question : why on earth do you needoperator<<(ostringstream&,...)`?
When you write
os << tuple_.get<0>();
there is no function that matches both parameters. Instead the compiler has a choice to apply an implicit conversion on either parameter
std::ostream << int
or
std::ostringstream << MyTuple
The latter would happen with the boost::tuple constructor that can take any number of arguments up to number of tuple elements. (And with float it fails, because float is convertible to int.)
When overloading stream operators, use the base class as the left hand side (ostream or even basic_ostream<CharT, Traits>.
Edit: You could disambiguate the call by casting the first argument.
ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)
{
static_cast<std::ostream&>(os_) << tuple_.get<0>();
static_cast<std::ostream&>(os_) << tuple_.get<1>();
static_cast<std::ostream&>(os_) << tuple_.get<2>(); // Error because no matching operator. Fine.
return os_;
}
However, overloading the operator with ostringstream is still a bad idea, because it won't work with operator chaining.
MyTuple a, b;
ostringstream ss;
ss << a << ' ' << b;
will invoke:
1) ostringstream& operator<<(ostringstream& os_, Mytuple tuple_)
2) ostream& ostream::operator<<(char)
3) ostream& operator<<(ostream&&, boost::tuple<int,float,Myclass>
All those people telling you to use ::std::ostream for the type instead of ::std::ostringstream are absolutely correct. You shouldn't be using ::std::ostringstream that way.
But my main beef with your code is the distressing lack of generality. It only works for one particular tuple type, and not all of them.
So I wrote an operator << for ::std::tuple in C++0x that works for any tuple who's members can be individually written using operator <<. It can probably be translated relatively easily to work with Boost's tuple type. Here it is:
template < ::std::size_t fnum, typename tup_type>
void print_fields(::std::ostream &os, const tup_type &val)
{
if (fnum < ::std::tuple_size<tup_type>::value) {
::std::cerr << "Fred " << fnum << '\n';
os << ::std::get<fnum, tup_type>(val);
if (::std::tuple_size<tup_type>::value > (fnum + 1)) {
os << ", ";
}
print_fields<fnum + 1, tup_type>(os, val);
}
}
template < ::std::size_t fnum, typename... Elements>
class field_printer;
template <typename... Elements>
class field_printer<0, Elements...> {
public:
typedef ::std::tuple<Elements...> tup_type;
static void print_field(::std::ostream &os, const tup_type &val) {
}
};
template < ::std::size_t fnum, typename... Elements>
class field_printer {
public:
typedef ::std::tuple<Elements...> tup_type;
static void print_field(::std::ostream &os, const tup_type &val) {
constexpr auto tupsize = ::std::tuple_size<tup_type>::value;
os << ::std::get<tupsize - fnum, Elements...>(val);
if (fnum > 1) {
os << ", ";
}
field_printer<fnum - 1, Elements...>::print_field(os, val);
}
};
template <class... Types>
::std::ostream &operator <<(::std::ostream &os, const ::std::tuple<Types...> &val)
{
typedef ::std::tuple<Types...> tup_type;
os << '(';
field_printer< ::std::tuple_size<tup_type>::value, Types...>::print_field(os, val);
return os << ')';
}
This prints out the tuple as "(element1, element2, ...elementx)".

Conversion operator template specialization

Here's a largely academic exercise in understanding conversion operators, templates and template specializations. The conversion operator template in the following code works for int, float, and double, but fails when used with std::string... sort of. I've created a specialization of the conversion to std::string, which works when used with initialization std::string s = a;, but fails when used with a cast static_cast<std::string>(a).
#include <iostream>
#include <string>
#include <sstream>
class MyClass {
int y;
public:
MyClass(int v) : y(v) {}
template <typename T>
operator T() { return y; };
};
template<>
MyClass::operator std::string() {
std::stringstream ss;
ss << y << " bottles of beer.";
return ss.str();
}
int main () {
MyClass a(99);
int i = a;
float f = a;
double d = a;
std::string s = a;
std::cerr << static_cast<int>(a) << std::endl;
std::cerr << static_cast<float>(a) << std::endl;
std::cerr << static_cast<double>(a) << std::endl;
std::cerr << static_cast<std::string>(a) << std::endl; // Compiler error
}
The above code generates a compiler error in g++ and icc, both complaining that no user-defined conversion is suitable for converting a MyClass instance to a std::string on the static_cast (C-style casts behave the same).
If I replace the above code with explicit, non-template versions of the conversion operator, everything is happy:
class MyClass {
int y;
public:
MyClass(int v) : y(v) {}
operator double() {return y;}
operator float() {return y;}
operator int() {return y;}
operator std::string() {
std::stringstream ss;
ss << y << " bottles of beer.";
return ss.str();
}
};
What is wrong with my template specialization for std::string? Why does it work for initialization but not casting?
Update:
After some template wizardry by #luc-danton (meta-programming tricks I'd never seen before), I have the following code working in g++ 4.4.5 after enabling experimental C++0x extensions. Aside from the horror of what is being done here, requiring experimental compiler options is reason enough alone to not do this. Regardless, this is hopefully as educational for others as it was for me:
class MyClass {
int y;
public:
MyClass(int v) : y(v) {}
operator std::string() { return "nobody"; }
template <
typename T
, typename Decayed = typename std::decay<T>::type
, typename NotUsed = typename std::enable_if<
!std::is_same<const char*, Decayed>::value &&
!std::is_same<std::allocator<char>, Decayed>::value &&
!std::is_same<std::initializer_list<char>, Decayed>::value
>::type
>
operator T() { return y; }
};
This apparently forces the compiler to choose the conversion operator std::string() for std::string, which gets past whatever ambiguity the compiler was encountering.
You can reproduce the problem by just using
std::string t(a);
Combined with the actual error from GCC (error: call of overloaded 'basic_string(MyClass&)' is ambiguous) we have strong clues as to what may be happening: there is one preferred conversion sequence in the case of copy initialization (std::string s = a;), and in the case of direct initialization (std::string t(a); and static_cast) there are at least two sequences where one of them can't be preferred over the other.
Looking at all the std::basic_string explicit constructors taking one argument (the only ones that would be considered during direct initialization but not copy initialization), we find explicit basic_string(const Allocator& a = Allocator()); which is in fact the only explicit constructor.
Unfortunately I can't do much beyond that diagnostic: I can't think of a trick to discover is operator std::allocator<char> is instantiated or not (I tried SFINAE and operator std::allocator<char>() = delete;, to no success), and I know too little about function template specializations, overload resolution and library requirements to know if the behaviour of GCC is conforming or not.
Since you say the exercise is academic, I will spare you the usual diatribe how non-explicit conversion operators are not a good idea. I think your code is a good enough example as to why anyway :)
I got SFINAE to work. If the operator is declared as:
template <
typename T
, typename Decayed = typename std::decay<T>::type
, typename = typename std::enable_if<
!std::is_same<
const char*
, Decayed
>::value
&& !std::is_same<
std::allocator<char>
, Decayed
>::value
&& !std::is_same<
std::initializer_list<char>
, Decayed
>::value
>::type
>
operator T();
Then there is no ambiguity and the code will compile, the specialization for std::string will be picked and the resulting program will behave as desired. I still don't have an explanation as to why copy initialization is fine.
static_cast here is equivalent of doing std::string(a).
Note that std::string s = std::string(a); doesn't compile either. My guess is, there are plenty of overloads for the constructor, and the template version can convert a to many suitable types.
On the other hand, with a fixed list of conversions, only one of those matches exactly a type that the string's constructor accepts.
To test this, add a conversion to const char* - the non-templated version should start failing at the same place.
(Now the question is why std::string s = a; works. Subtle differences between that and std::string s = std::string(a); are only known to gods.)