why explicit operator std::string does not work - c++

I have a class which has a conversion operator for type std::string marked as explicit. Here is the class
class MyClass {
public:
// methods ...
explicit operator std::string() const {
// convert to string ...
}
}
Problem is when I use static_cast on a variable of type MyClass I get the error "No matching conversion for static_cast from 'MyClass' to 'std::string aka …" I seem to have the same problem when I define conversion operators for any custom type. Is the explicit modifier only defined for conversion to primitive types or is this another compiler bug.

Here is an example
#include <iostream>
#include <string>
class MyClass {
public:
// methods ...
explicit operator std::string() const {
return "Hello World";
}
};
int main()
{
MyClass obj;
std::cout << static_cast<std::string>( obj ) << std::endl;
return 0;
}
The output is
Hello World

Problem solved by updating to the latest version of LLVM, which fully supports all C++11 features.

Related

C++ Overloading conversion operator hack

I am trying to implement a hack where I am trying to overload return. I will let the code talk.
namespace tcn
{
class foo
{
std::string blah;
public:
class proxy
{
int intval;
std::string strval;
public:
proxy(int intv, std::string strv) : intval(intv), strval(strv) {};
operator int() const { return intval; }
operator std::string() const { return strval; }
};
foo();
~foo();
proxy get();
};
foo::foo()
{
// Create stuff //
}
foo::~foo()
{
// Destroy stuff //
}
foo::proxy foo::get()
{
int intval = 911;
std::string strval = "Hello!?";
proxy temp(intval, strval);
return temp;
}
}
int main()
{
tcn::foo bar;
std::string ts = bar.get(); // OK
int ti = bar.get(); // OK
ti = bar.get(); // OK
ts = bar.get(); // Compile error here
return 0;
}
If I try to compile the code it gives an error like following
error: ambiguous overload for 'operator=' (operand types are
'std::string {aka std::basic_string}' and 'tcn::foo::proxy')
ts = bar.get();
I was wondering how to overcome this. I have seen other ways to implement this by using 'hints' ,But am trying to give an easy interface to the user. So I am looking for a simple assignment from the user side. How else could this be implemented?
Thank you in advance and if this looks lame- I apologize. I am no that good in C++.
The call is ambiguous because std::string::operator= has overloads taking std::string and char. Both of these can be called using a tcn::proxy: the first using the std::string conversion operator, and the second using the int conversion operator followed by an integral conversion.
Usually overload resolution would prefer the implicit conversion sequence with no necessary standard conversions over the one requiring an integral conversion, but this choice is only considered if the two conversion sequences go through the same function. Since you have two separate paths going through two different functions, the call is ambiguous.
The solution is to not use integral conversions for things like that; it just leads to weird edge cases, surprising behaviour and hidden bugs.

Why does this assignment operation results in ambiguous function call?

Consider following program it compiles and runs fine:
#include <iostream>
#include <string>
using std::string;
struct BB
{
// generic cast
template<typename T>
operator T() const
{
std::cout<<"Generic cast\n";
return 0;
}
// string cast
operator string() const
{
std::cout<<"string cast\n";
return string("hello");
}
};
int main()
{
BB b;
string s = b; // copy constructor
}
But If I slightly change the main() function's code in like following:
int main()
{
BB b;
string s;
s = b;
}
Compiler give following error message (See live demo here)
[Error] ambiguous overload for 'operator=' (operand types are 'std::string {aka std::basic_string<char>}' and 'BB')
Why this call is ambiguous? What is the reason behind that? It looks like there are so many overloaded operator= like one for char, one for char*, one for const char* etc. That's the above program puts compiler into ambiguity.
Your problem is your template conversion operator.
template<typename T>
operator T() const
{
std::cout << "Generic cast\n";
return 0;
}
Allows BB to be converted to anything. Because of that all of the overloads of std::string::operator= that take a different type can be considered. Since they are all valid there is no way to resolve the ambiguity and you get the error.
If you removed the template conversion then it will compile. The template conversion could also be marked as explicit and then you can use a static_cast to the type you want.
You call operator =, but it would be the same if it were a regular function:
void f(int x);
void f(const string &y);
int main() {
BB b;
f(b);
return 0;
}
Since BB can be cast in either int or string, the compiler has no idea which f function to call.
The only reason why your first example works is because the copy constructor is called there, and it only takes const string& as an argument, so there's no multiple choices.

Implicit construction of std::string does not happen during copy-initialization

I'm attempting to copy-initialize my CObj class as follows in the main() function:
#include <string>
#include <iostream>
class CObj
{
public:
CObj(std::string const& str) : m_str(str) { std::cout << "constructor" << std::endl; }
~CObj() { std::cout << "destructor" << std::endl; }
private:
std::string m_str;
};
int main()
{
CObj obj = "hello";
std::cout << "done" << std::endl;
}
However, the line CObj obj = "hello" fails to compile even though std::string is implicitly constructible from a char const*. According to my understanding here, this should work. Any reason why it doesn't? If I do this it works:
CObj obj = std::string("hello");
The literal "Hello" has type const char[6] : in order to call your constructor, two conversions are required : one to std::string and a second one to CObj .
But C++ only allows one user-defined conversion when doing an implicit conversion :
C++ Standard section § 12.3/4 [class.conv]
Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions and are used for implicit type conversions
[...]
At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
This is why this works :
CObj obj = std::string("hello");
Or this:
CObj obj("hello");
Or you could provide a constructor that accepts a const char* :
CObj(const char* cstr) : m_str(cstr) { ... }
I would always advise to make such constructors explicit to avoid unwanted implicit conversions, unless it really brings something to the user of the class.
You are limited to at most one user-defined conversion when instantiating an object (i.e. char[6] -> std::string and std::string -> CObj is one conversion too many).
To fix:
int main()
{
using namespace std::literals::string_literals; // this is necessary
// for the literal conversion
CObj obj = "hello"s; // note the extra "s", (which constructs a std::string)
std::cout << "done" << std::endl;
}

Implicit conversion from user-defined type to primitive type in C++

I am able to find plenty of information about implicit conversion from, say, an int to a user defined type. i.e. if a constructor takes an int as its parameter and is not prefaced by "explicit" then implicit conversions can occur.
What if I want my class to implicitly convert to an int?
For example, what function needs to be added either inside or outside of SimpleClass such that the main function will compile and output "1" to the console? (see comments)
#include <iostream>
class SimpleClass
{
private:
int m_int;
public:
SimpleClass(int value)
: m_int(value) {}
};
int main(int argc, const char * argv[])
{
SimpleClass simpleclass(1);
int i = simpleclass; // does not complile
std::cout << i << std::endl; // should output "1" to the console
return 0;
}
Implicit conversions can be defined in two ways:
non-explicit single-argument constructor.
non-explicit conversion function (a.k.a. conversion operator), N3337 12.3.2
The later allows defining conversion from class type to primitive type. Just add
class SimpleClass {
// ...
operator int() const;
};
SimpleClass::operator int() const
{
return m_int;
}
The technical.
Conversion to (almost) any type T can be performed by an operator T member function.
It is by default invoked implicitly, and if you declare it const it can also be invoked on a const object.
Thus:
struct MyType
{
operator int() const { return 1; }
};
Problems…
Having a an implicit conversion to basic type allows free play for all the built-in operators, including
Arithmetic operators.
Boolean operators.
Relational operators.
So you better make sure that all this works the way you want.
And that can be a lot of work!
There are also potential problems with overload resolution for calls involving instances of your type.
In short, implicit conversion to int, or pointer, or any built-in type, usually costs more than it's worth.
An exception where it can be worthwhile is when a class is used a lot, in a library.
What you can do about it.
Avoid implicit conversion, but do offer explicit conversion.
The best general explicit conversion is, IMHO, a named member function.
An alternative is an operator T prefixed with the keyword explicit, which is supported for this use in C++11 and later (in C++03 it could only be used on constructors).
If you want output via << to behave as if an implicit conversion is performed, then just define an operator<<.
And similarly for other situations where an implicit conversion would appear to be a general solution: just define what's appropriate for that specific situation, and avoid introducing a general implicit conversion.
To provide implicit conversion to a built-in type and yet avoid the “free for all” for built-in operators, you can use a templatized type conversion, e.g. like this:
#include <iostream>
template< class A, class B > struct Is_t_;
template< class Type > struct Is_t_<Type, Type> { using T = void; };
template< class A, class B >
using If_is_ = typename Is_t_<A, B>::T;
struct Bad_string
{
operator const char* () const { return "666!"; }
Bad_string( char const* = 0 ) {}
};
auto operator==( Bad_string const&, Bad_string const& )
-> bool
{ return true; }
struct Good_string
{
template< class Type, class Enabled_ = If_is_<const char*, Type>>
operator Type() const { return "42 :)"; }
Good_string( char const* = 0 ) {}
};
auto operator==( Good_string const&, Good_string const& )
-> bool
{ return true; }
#if defined( DO_GOOD )
using String = Good_string;
#elif defined( DO_BAD )
using String = Bad_string;
#else
# error "Define either DO_GOOD or DO_BAD, please."
#endif
auto main() -> int
{
String a, b;
(void) (a == "alfalfa"); // Errs for Bad_string
(void) (a + 1); // Compiles for Bad_string.
}
Ironically, when DO_GOOD is defined this code crashes the Visual C++ 2015 update 1 compiler, a so called “ICE” (Internal Compiler Error).
A workaround for that compiler is to instead define If_is_ as
template< class A, class B >
using If_is_ = std::enable_if_t< std::is_same<A, B>::value >;
To allow your class to be converted to an int, implement
operator int() const
{
return m_int;
}

Operator overloading c++ (<<)

This the below program i have written for some test.
class tgsetmap
{
public:
std::map<std::string,std::string> tgsetlist;
void operator<<(const char *str1,const char *str2)
{
tgsetlist.insert( std::map<std::string,std::string>::value_type(str1,str2));
}
};
int main()
{
tgsetmap obj;
obj<<("tgset10","mystring");
obj.tgsetlist.size();
}
This throws a compilation error:
"test.cc", line 10: Error: Illegal number of arguments for tgsetmap::operator<<(const char, const char*).
"test.cc", line 22: Error: The operation "tgsetmap << const char*" is illegal.
2 Error(s) detected.*
Am i wrong some where?
You can't force operator<< to take two arguments on right-hand side. The following code:
obj<<("tgset10","mystring");
does not work as a function call with two arguments but instead just uses the , operator. But it's probably not what you are interested in.
If you need to pass two arguments to the << operator, you need to wrap them in some other (single) type. For example, you could use the standard std::pair, i.e. std::pair<const char*, const char*>.
But note that the operator<< should also return some reasonable type suitable for << chaining. That would probably be a tgsetmap& in your case. The following version should work fine:
#include <map>
#include <string>
#include <iostream>
class tgsetmap
{
public:
typedef std::map<std::string, std::string> list_type;
typedef list_type::value_type item_type;
list_type tgsetlist;
tgsetmap& operator<<(item_type item)
{
tgsetlist.insert(item);
return *this;
}
};
int main()
{
tgsetmap obj;
obj << tgsetmap::item_type("tgset10","mystring")
<< tgsetmap::item_type("tgset20","anotherstring");
std::cout << obj.tgsetlist.size() << std::endl;
}
Note that I've added typedefs to not have to repeat the type names over and over again. I've also made operator<< return a tgsetmap& so that << could be chained (used like in the modified main() above). And finally, I've reused the std::map<...>::value_type to make it simpler but you could also use any other type of your own.
But I believe that you may prefer using a regular method instead. Something like:
void add(const char *str1, const char *str2)
{
tgsetlist.insert( std::map<std::string, std::string>::value_type(str1, str2));
}
(inside the class declaration), and then:
obj.add("tgset10", "mystring");
The operator<< inside of a class must be overloaded like this:
T T::operator <<(const T& b) const;
If you want to overload it with 2 arguments, you can do it outside of a class:
T operator <<(const T& a, const T& b);
My compiler, for example, gives a more detailed error message for the code you posted:
If you are not sure about an operator overloading syntax, there is a wiki article about it.
Yes. operator << is binary operator. not ternary. not forget about this pointer.
As mentioned, the << is binary operator, so there is no way it can take more than two args(One should be this if you are declaring inside the class or a LHS if you are declaring outside the class). However you can accomplish the same functionality by doing obj<<"tgset10". <<"mystring";. But since << is a binary operator, you have to do some hack for this.
For this, I ve assigned a static variable op_count, where in I will determine if it is the value or the type. And another static variable temp_str to store the previous value across invocations.
class tgsetmap
{
public:
std::map<std::string,std::string> tgsetlist;
static int op_count = 0;
static const char *temp_str;
tgsetmap& operator<<(const char *str)
{
op_count++;
if (op_count%2 != 0) {
temp_str = str;
}
else {
tgsetlist.insert( std::map<std::string,std::string>::value_type(temp_str,str));
}
return this;
}
};
So you can do
int main()
{
tgsetmap obj;
obj<<"tgset10"<<"mystring";
obj.tgsetlist.size();
}
Or simply you can embed the value and type in the same string using some separator,
value:type = separator is :
value_type = separator is _.