implicit constructor not recognized - c++

I'm trying to figure out why the compiler does not deduce that it should use the string constructor of Obj.
Shouldn't implicit construction be enabled for this case? (i pass const reference string and const reference Obj)
Isn't that the entire idea of implicit construction? what am i missing here?
class Obj
{
public:
Obj(const std::string& str)
{
}
void operator+=(const Obj& other)
{
cout << "operator+=" << endl;
}
Obj& operator++()
{
operator +=("something"); // error
return *this;
}
};
i'm compiling with gcc 4.8.2,
thanks!

The conversion from the string literal "something" to Obj would require two user-defined conversions: one from the string literal to an std::string, and one from std::string to Obj using your constructor. Implicit conversion sequences are limited to containing only one user-defined conversion.
The best way to solve this problem is to add another constructor that takes a C string,
Obj(const char* str)
If you don't want to add such a constructor and you want to perform this conversion just once, you could:
Write operator+=(std::string("something")) (perform the first conversion explicitly)
(C++14) Write operator+=("something"s), which uses the new user-defined literal suffix s to construct std::string

You aren't allowed more than one user defined conversion. You have two, char* to std::string, and std::string to Obj. You can fix this by passing an std::string to your operator+=:
Obj& operator++()
{
operator +=(std::string("something"));
return *this;
}
Alternatively, you can add an implicit constructor that takes a const char*"
Obj(const char* str)

Related

Supposedly ambiguous explicit conversion operator in MSVC, not in gcc or clang

Consider the following class that implements a user-conversion to std::string and const char*:
class String {
public:
String(std::string_view s) : str{s} {}
operator std::string() const {
return str;
}
operator const char*() const {
return str.c_str();
}
private:
std::string str;
};
int main()
{
static_assert(std::is_convertible_v<String, std::string>);
String s("Hello World");
auto x = static_cast<std::string>(s);
return 0;
}
MSVC tells me that static_casting to std::string is ambiguous, while clang and gcc do not: https://godbolt.org/z/7de5YvTno
<source>(20): error C2440: 'static_cast': cannot convert from 'String' to 'std::string'
<source>(20): note: No constructor could take the source type, or constructor overload resolution was ambiguous
Compiler returned: 2
Which compiler is right?
As a follow-up question: I can fix the ambiguity by marking the conversion operations explicit. But then std::is_convertible_v<String, std::string> returns false. Is there a type trait is_explicitly_convertible or is_static_castible or similar?
PS: I know the simplest and cleanest solution would be to have a non-copying conversion operator to const std::string&, but I still want to understand why MSVC rejects the code.
This is CWG2327.
As written, the direct-initialization specified for static_cast strictly considers constructors for std::string, and the String argument requires incomparable user-defined conversions to satisfy either the move constructor or the converting constructor from const char*. Note that, regardless of the set of conversion functions available, a constructor must be called, which is the missed copy elision mentioned in the issue.
The intent is that constructors and conversion functions are simultaneously considered for the actual initialization of x. This is a bit unusual in that member functions of std::string and String are in the same overload set, but it resolves the ambiguity because calling operator std::string is an exact match for the (implied object) argument, and it allows x to be the return value from that function in the ordinary C++17 sense.
MSVC is implementing the standard as written, while GCC and Clang are implementing (something like) the intended resolution.
(std::is_constructible is more or less the direct-initialization trait you also asked about.)

Clang ambiguity with custom conversion operator

I've been developing a kind of adapter class, when I encountered a problem under clang. When both conversion operators for lvalue-reference and rvalue reference are defined you get an ambiguity compilation error trying to move from your class (when such code should be fine, as
operator const T& () const&
is allowed only for lvalues AFAIK). I've reproduced error with simple example:
#include <string>
class StringDecorator
{
public:
StringDecorator()
: m_string( "String data here" )
{}
operator const std::string& () const& // lvalue only
{
return m_string;
}
operator std::string&& () && // rvalue only
{
return std::move( m_string );
}
private:
std::string m_string;
};
void func( const std::string& ) {}
void func( std::string&& ) {}
int main(int argc, char** argv)
{
StringDecorator my_string;
func( my_string ); // fine, operator std::string&& not allowed
func( std::move( my_string ) ); // error "ambiguous function call"
}
Compiles fine on gcc 4.9+, fails on any clang version.
So the question: is there any workaround? Is my understanding of const& function modifier right?
P.S.: To clarify - the question is about fixing StringDecorator class itself (or finding the workaround for such class as if were a library code). Please refrain providing answers that call operator T&&() directly or specify conversion type explicitly.
The problem comes from the selection of the best viable function. In the case of the second func call, it implies the comparison of 2 user-defined conversion sequence. Unfortunately, 2 user-defined conversion sequence are undistinguishable if they do not use the same user-defined conversion function or constructor C++ standard [over.ics.rank/3]:
Two implicit conversion sequences of the same form are indistinguishable conversion sequences unless one of
the following rules applies:
[...]
User-defined conversion sequence
U1
is a better conversion sequence than another user-defined conversion
sequence
U2
if they contain the same user-defined conversion function or constructor [...]
Because a rvalue can always bind to a const lvalue reference, you will in any case fall on this ambiguous call if a function is overloaded for const std::string& and std::string&&.
As you mentioned it, my first answer consisting in redeclaring all functions is not a solution as you are implementing a library. Indeed it is not possible to define proxy-functions for all functions taking a string as argument!!
So that let you with a trade off between 2 imperfect solutions:
You remove operator std::string&&() &&, and you will loose some optimization, or;
You publicly inherit from std::string, and remove the 2 conversion functions, in which case you expose your library to misuses:
#include <string>
class StringDecorator
: public std::string
{
public:
StringDecorator()
: std::string("String data here" )
{}
};
void func( const std::string& ) {}
void func( std::string&& ) {}
int main(int argc, char** argv)
{
StringDecorator my_string;
func( my_string ); // fine, operator std::string&& not allowed
func( std::move( my_string )); // No more bug:
//ranking of standard conversion sequence is fine-grained.
}
An other solution is not to use Clang because it is a bug of Clang.
But if you have to use Clang, the Tony Frolov answer is the solution.
Oliv answer is correct, as standard seems to be quite clear in this case. The solution I've chosen at a time was to leave only one conversion operator:
operator const std::string& () const&
The problem exists because both conversion operators are considered viable. So this can be avoided by changing type of implicit argument of lvalue conversion operator from const& to &:
operator const std::string& () & // lvalue only (rvalue can't bind to non-const reference)
{
return m_string;
}
operator std::string&& () && // rvalue only
{
return std::move( m_string );
}
But this breaks conversion from const StringDecorator, making its usage awkward in typical cases.
This broken solution led me thinking if there is a way to specify member function qualifier that will make conversion operator viable with const lvalue object, but not with the rvalue. And I've managed to achieve this by specifying implicit argument for const conversion operator as const volatile&:
operator const std::string& () const volatile& // lvalue only (rvalue can't bind to volatile reference)
{
return const_cast< const StringDecorator* >( this )->m_string;
}
operator std::string&& () && // rvalue only
{
return std::move( m_string );
}
Per [dcl.init.ref]/5, for a reference to be initialized by binding to
an rvalue, the reference must be a const non-volatile lvalue
reference, or an rvalue reference:
While lvalue reference and const lvalue reference can bind to const volatile reference. Obviously volatile modifier on member function serves completely different thing. But hey, it works and sufficient for my use-case. The only remaining problem is that code becomes misleading and astonishing.
clang++ is more accurate. Both func overloads are not exact matches for StringDecorator const& or StringDecorator&&. Thus my_string can not be moved. The compiler can not choose between possible transforms StringDecorator&& --> std::string&& --> func(std::string&&) and StringDecorator&& -> StringDecorator& --> std::string& --> func(const std::string&). In other words the compiler can not determine on what step it should apply cast operator.
I do not have g++ installed to check my assumption. I guess it goes to the second way, since my_string can not be moved, it applies the cast operator const& to StringDecorator&. You can check it if you add debug output into bodies of cast operators.

Overloaded Bool/String Ambiguity

Why is C++ casting the string literal I pass in as a bool rather than a string?
#include <iostream>
using namespace std;
class A
{
public:
A(string v)
{
cout << v;
}
A(bool v)
{
cout << v;
}
};
int main()
{
A("hello");
return 0;
}
Output: 1
Is it because the compiler isn't smart enough to make the jump from char * to string and rather just assumes that bool is the closest thing to a pointer? Is my only option to make an explicit char * constructor that basically does the exact same thing as the string constructor?
If you have C++11 you can use a delegating constructor:
A(char const* s) : A(std::string(s)) { }
The reason the boolean converting-constructor is chosen over the one for std::string is because the conversion from char const* to bool is a standard conversion while the one to std::string is a user-defined conversion. Standard conversions have a greater rank than user-defined conversions.
With
A(string("hello"));
it will give the expected result.
Why is it so ?
It's because of the standard conversions:
"hello" is understood as a const char* pointer
this pointer can be converted to a bool (section 4.12 of the standard: "A prvalue of (...) pointer (...) type can be converted to a prvalue of type bool." )
the conversion from "hello" to string is not considered because section 12.3 of standard explains that "Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions" and "User-defined conversions are applied only where they are unambiguous". Wouldn't you have the bool constructor the std::string conversion would be done implicitely.
How to get what you expected ?
Just add the missing constructor for string litterals:
A(const char* v)
{
cout << v; // or convert v to string if you want to store it in a string member
}
Of course, instead of rewriting a constructor from scratch, you could opt for a delegate as suggested by 0x499602D2 in another answer.
Recently I passed this problem too, let me share another way.
You can change the bool constructor to a unsigned char. So the decay and implict conversion of string literal don't happen, and the std::string constructor takes place.
class A
{
public:
A(string v)
{
cout << v;
}
A(unsigned char v)
{
cout << static_cast<bool>(v);
}
};
int main()
{
A("Hello"); // <- Call A(string)
A(false); // <- Call A(unsigned char)
}
This way you don't need to provide always overloads to std::string and const char* neither making code bloat constructing the std::string at the client call site.
I don't claim that's better, but it's simpler.
When selecting an overloaded method this is the order that the compiler tries:
Exact match
Promotion
Standard numerical conversion
User defined operators
A pointer doesn't promote to bool but it does convert to bool. A char* uses an std operator to convert to std::string. Note that if char* used the same number in this list to convert to both bool and std::string it would be ambiguous which method the compiler should choose, so the compiler would throw an "ambiguous overload" error.
I would throw my weight behind 0x499602D2's solution. If you have C++11 your best bet is to call: A(char* s) : A(std::string(s)){}
If you don't have C++11 then I would create an A(char* s) constructor and abstract the logic of the A(std::string) constructor into a method and call that method from both constructors.
http://www.learncpp.com/cpp-tutorial/76-function-overloading/

Why this constructor is being called?

I'm confused about following program about why it calls first constructor.
class A
{
public:
A(const char *c="\0")
{
cout<<"Constructor without arg";
}
A(string c)
{
cout<<"New one";
}
};
int main()
{
A a="AMD";
return 0;
}
Output is
Constructor without arg
"AMD" is a const char[], which is implicitly converted to const char*, so the first constructor [A(const char *c="\0")]is the best match.
Note that A(const char *c="\0") is not a constructor without an argument, it's a constructor which takes a single const char* as an argument, and has an optional default value to use when a const char* isn't specified. In this case, you're passing a const char*, so it uses it.
Because a string literal is of type const char[] which implicitly converts to const char* which is preferred over the user-defined conversion std::string(const char*) (This is not really the signature of the string constructor, but enough for this explanation).
Also: initialization is not assignment. This is why a constructor and not operator= is called in the first place.
The preferred syntax for assignment in C++11 would be A a{"ASDF"};. It makes things more uniform.
You're calling the constructor with a const char * because that is what "AMD" is. It's not a string. If you put A a(string("AMD")) it will work.

operator std::string() const?

Can somebody tell me what precisely
operator std::string()
stands for?
It is a conversion operator that allows the object to be explicitly or implicitly casted to std::string. When such a cast occurs, the operator is invoked and the result of the cast is the result of the invocation.
As an example of an implicit cast, suppose you had a function that accepted type std::string or const std::string&, but not the given object type. Passing your object to that function would result in the conversion operator being invoked, with the result passed to the function instead of your type.
It is a cast operator. Any class that defines this type can be used anywhere a std::string is required. For instance,
class Foo {
public:
operator std::string() const { return "I am a foo!"; }
};
...
Foo foo;
std::cout << foo; // Will print "I am a foo!".
Cast operators are almost always a bad idea, since there is invariably a better way to achieve the same result. In the above case, you are better off defining operator<<(std::ostream&, const Foo&).