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.
Related
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.)
Consider the following class:
class foo
{
public:
constexpr operator bool() const noexcept;
constexpr operator void * &() noexcept;
constexpr operator void * const &() const noexcept;
constexpr operator void const * &() noexcept;
constexpr operator void const * const &() const noexcept;
};
A foo object would be used like this:
void bar(bool);
// ...
foo f;
bar(f); // error C2664: cannot convert argument 1 from 'foo' to 'bool'
// message : Ambiguous user-defined-conversion
// message : see declaration of 'bar'
The issue is that the operator bool is not considered because of its constness. If I make another function without the const qualifier, the problem is solved. If I make f const, the problem is solved as well. If I explicitly cast f to bool, the problem is solved.
Sample to work with
What are my other options and what is causing this ambiguity?
First you should have a look at this question regarding conversion precedence in C++.
Then as pointed by some comments, operator bool() was not selected by the compiler for this conversion and the ambiguous resolution message is not about it.
The ambiguity comes from constexpr operator void * &() noexcept versus constexpr operator void const * &() noexcept. The idea being: let's try to convert a non const object to something and then see if this something can be converted to bool.
Also operator void const*() and operator void*() are redundant because there is no situation when you can call one and not the other and you can get a const void* from a void*.
Also returning void const * const & does not have any advantage over returning void const* as you won't be able to modify the reference. Returning a pointer instead of a reference to a pointer at worst does not change anything, at best, prevent you from doing a double indirection.
My advice would be to remove the non const operators and replace them by an explicit setter when you want to change the underlying pointer stored in foo. But that might not be the solution you are looking for depending on your actual problem and your design choices.
Take a look at the following, simplified, code:
struct foo {
constexpr operator bool() const noexcept; // (1)
constexpr operator void * &() noexcept; // (2)
constexpr operator void const * &() noexcept; // (3)
};
void bar(bool);
// ...
foo f;
bar(f);
The implicit conversion rules state the following:
Order of the conversions
Implicit conversion sequence consists of the following, in this order:
zero or one standard conversion sequence;
zero or one user-defined conversion;
zero or one standard conversion sequence.
The conversion of the argument in bar is one user-defined conversion, followed by one standard conversion sequence.
Continued:
... When converting from one built-in type to another built-in type, only one standard conversion sequence is allowed.
A standard conversion sequence consists of the following, in this order:
zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion;
zero or one numeric promotion or numeric conversion;
zero or one function pointer conversion; (since C++17)
zero or one qualification adjustment.
There are planty of types that can be used where a bool is required, and pointers are amongst them, so for overloads #2 and #3 all that is required is one "lvalue-to-rvalue conversion". To use overload #1 the compiler will have to perform a "qualification adjustment" (const bool to bool).
How to fix this:
Remove ambiguity by adding the constexpr operator bool() noexcept;, which fits bar exactly.
So I have an existing library that provides a string type.
It implicitly converts to-from C style strings like so:
struct TypeIDoNotOwn {
TypeIDoNotOwn() {}
TypeIDoNotOwn(TypeIDoNotOwn const&) {}
TypeIDoNotOwn(char const*) {}
TypeIDoNotOwn& operator=(TypeIDoNotOwn const&) {return *this;}
TypeIDoNotOwn& operator=(char const*) {return *this;}
operator char const*() const {return nullptr;}
};
it has other methods, but I do not think they are important. These methods have bodies, but my problem doesn't involve them, so I have stubbed them out.
What I want to do is to create a new type that can be used relatively interchangably with the above type, and with "raw string constants". I want to be able to take an instance of TypeIDoNotOwn, and replace it with TypeIDoOwn, and have code compile.
As an example, this set of operations:
void test( TypeIDoNotOwn const& x ) {}
int main() {
TypeIOwn a = TypeIDoNotOwn();
TypeIDoNotOwn b;
a = b;
b = a;
TypeIOwn c = "hello";
TypeIDoNotOwn d = c;
a = "world";
d = "world";
char const* e = a;
std::pair<TypeIDoNotOwn, TypeIDoNotOwn> f = std::make_pair( TypeIOwn(), TypeIOwn() );
std::pair<TypeIOwn, TypeIOwn> g = std::make_pair( TypeIDoNotOwn(), TypeIDoNotOwn() );
test(a);
}
If I replace TypeIOwn with TypeIDoNotOwn above, it compiles. How do I get it to compile with TypeIOwn without modifying TypeIDoNotOwn? And without having to introduce any casts or changes other than the change-of-type at point of declaration?
My first attempt looks somewhat like this:
struct TypeIOwn {
TypeIOwn() {}
operator char const*() const {return nullptr;}
operator TypeIDoNotOwn() const {return {};}
TypeIOwn( TypeIOwn const& ) {}
TypeIOwn( char const* ) {}
TypeIOwn( TypeIDoNotOwn const& ) {}
TypeIOwn& operator=( char const* ) {return *this;}
TypeIOwn& operator=( TypeIOwn const& ) {return *this;}
TypeIOwn& operator=( TypeIDoNotOwn const& ) {return *this;}
};
but I get a series of ambiguous overloads:
main.cpp:31:4: error: use of overloaded operator '=' is ambiguous (with operand types 'TypeIDoNotOwn' and 'TypeIOwn')
b = a;
~ ^ ~
main.cpp:9:17: note: candidate function
TypeIDoNotOwn& operator=(TypeIDoNotOwn const&) {return *this;}
^
main.cpp:10:17: note: candidate function
TypeIDoNotOwn& operator=(char const*) {return *this;}
and
/usr/include/c++/v1/utility:315:15: error: call to constructor of 'TypeIDoNotOwn' is ambiguous
: first(_VSTD::forward<_U1>(__p.first)),
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:40:51: note: in instantiation of function template specialization 'std::__1::pair<TypeIDoNotOwn, TypeIDoNotOwn>::pair<TypeIOwn, TypeIOwn>' requested here
std::pair<TypeIDoNotOwn, TypeIDoNotOwn> f = std::make_pair( TypeIOwn(), TypeIOwn() );
^
main.cpp:7:7: note: candidate constructor
TypeIDoNotOwn(TypeIDoNotOwn const&) {}
^
main.cpp:8:7: note: candidate constructor
TypeIDoNotOwn(char const*) {}
^
In my "real" code I have other operators, like += and ==, that have similar problems.
The scope of the real problem is large; millions of lines of code, and I want to swap out TypeIDoNotOwn for TypeIOwn at many thousands of locations, but not at many hundreds of others. And at thousands of locations they interact in a way that causes the conversion ambiguity.
I have solved the problem of a function taking TypeIDoNotOwn& at the 100s of spots where it happens by wrapping it with a macro that creates a temporary object that creates a TypeIDoNotOwn from the TypeIOwn, returns a reference to that, then when the temporary object is destroyed copies it back to the TypeIOwn. I want to avoid having to do a similar sweep to handle ==, +=, =, copy-construction, and similar situations.
Live example.
If I try to remove the operator TypeIDoNotOwn to clear up that ambiguity, other cases where the conversion need occur don't work right (as it requires 2 user-defined constructions to get from TypeIOwn to TypeIDoNotOwn), which then requires an explicit conversion to occur (at many 100s or 1000s of locations)
If I could make one conversion look worse than the other, it would work. Failing that, I could try fixing the non-operator= and copy-construct cases by overloading a free TypeIDoNotOwn == TypeIOwn operator with exact matching (and similar for other cases), but that doesn't get me construction, function calls, and assignment.
With the usual caveats that this is C++ and there's bound to be some clever workaround... no.
Let's go through your use cases. You want both copy initialization and copy assignment to work:
TypeIOwn a = ...;
TypeIDoNotOwn b = a; // (*)
TypeIDoNotOwn c;
c = a; // (*)
That necessitates:
operator TypeIDoNotOwn();
If you just provided operator const char*(), then assignment would work, but copy-initialization would fail. If you provided both, it's ambiguous as there's no way to force one conversion to be preferred to the other (the only real way to force conversion ordering would be to create type hierarchies, but you can't inherit from const char* so you can't really force that to work).
Once we get ourselves down to having just the one conversion function, the only code that doesn't work from the list of examples is:
const char* e = a; // error: no viable conversion
At which point, you'll have to add a member function:
const char* e = a.c_str();
Both pair constructions work fine with the one conversion function. But just by process of elimination, we can't have both.
There's no magic bullet, but you might get some improvement by declaring the conversion from TypeIOwn to TypeIDoNotOwn as explicit.
explicit operator TypeIDoNotOwn() const { return{}; }
This means you do have to make a change at each spot where this happens, but it does resolve the problem with "const char*" being equally valid for assignments. Is it worth the trade-off? You'll have to decide.
However, for incrementally changing the code base, I have had some luck in similar situations using a different strategy. I just set a #define flag and compile using entirely one or the other, and I can continue to code normally with TypeIDoNotOwn, while simultaneously making progress on making everything work with TypeIDoOwn.
#ifdef SOME_FLAG
struct TypeIOwn {...};
typedef TypeIOwn TypeIDoNotOwn;
#else
struct TypeIDoNotOwn {...};
#endif
You will have to test both for every update, until you finally make the plunge.
Since you say this is a string class, also consider the option of moving towards std::string, so your TypeIOwn becomes a thin wrapper for std::string, and no longer provides an implicit conversion to const char*. Instead, provide data(). You no longer have ambiguous conversions from TypeIOwn -> (const char* | TypeIDoNotOwn) -> TypeIDoNotOwn, because like std::string, you no longer allow implicit conversion to const char*, and any work you put into making the code work with this will pay off when you ditch both string classes entirely and use std::string.
C++11 makes it possible to overload member functions based on reference qualifiers:
class Foo {
public:
void f() &; // for when *this is an lvalue
void f() &&; // for when *this is an rvalue
};
Foo obj;
obj.f(); // calls lvalue overload
std::move(obj).f(); // calls rvalue overload
I understand how this works, but what is a use case for it?
I see that N2819 proposed limiting most assignment operators in the standard library to lvalue targets (i.e., adding "&" reference qualifiers to assignment operators), but this was rejected. So that was a potential use case where the committee decided not to go with it. So, again, what is a reasonable use case?
In a class that provides reference-getters, ref-qualifier overloading can activate move semantics when extracting from an rvalue. E.g.:
class some_class {
huge_heavy_class hhc;
public:
huge_heavy_class& get() & {
return hhc;
}
huge_heavy_class const& get() const& {
return hhc;
}
huge_heavy_class&& get() && {
return std::move(hhc);
}
};
some_class factory();
auto hhc = factory().get();
This does seem like a lot of effort to invest only to have the shorter syntax
auto hhc = factory().get();
have the same effect as
auto hhc = std::move(factory().get());
EDIT: I found the original proposal paper, it provides three motivating examples:
Constraining operator = to lvalues (TemplateRex's answer)
Enabling move for members (basically this answer)
Constraining operator & to lvalues. I suppose this is sensible to ensure that the "pointee" is more likely to be alive when the "pointer" is eventually dereferenced:
struct S {
T operator &() &;
};
int main() {
S foo;
auto p1 = &foo; // Ok
auto p2 = &S(); // Error
}
Can't say I've ever personally used an operator& overload.
One use case is to prohibit assignment to temporaries
// can only be used with lvalues
T& operator*=(T const& other) & { /* ... */ return *this; }
// not possible to do (a * b) = c;
T operator*(T const& lhs, T const& rhs) { return lhs *= rhs; }
whereas not using the reference qualifier would leave you the choice between two bads
T operator*(T const& lhs, T const& rhs); // can be used on rvalues
const T operator*(T const& lhs, T const& rhs); // inhibits move semantics
The first choice allows move semantics, but acts differently on user-defined types than on builtins (doesn't do as the ints do). The second choice would stop the assigment but eliminate move semantics (possible performance hit for e.g. matrix multiplication).
The links by #dyp in the comments also provide an extended discussion on using the other (&&) overload, which can be useful if you want to assign to (either lvalue or rvalue) references.
If f() needs a Foo temp that is a copy of this and modified, you can modify the temp this instead while you can't otherwise
On the one hand you can use them to prevent functions that are semantically nonsensical to call on temporaries from being called, such as operator= or functions that mutate internal state and return void, by adding & as a reference qualifier.
On the other hand you can use it for optimizations such as moving a member out of the object as a return value when you have an rvalue reference, for example a function getName could return either a std::string const& or std::string&& depending on the reference qualifier.
Another use case might be operators and functions that return a reference to the original object such as Foo& operator+=(Foo&) which could be specialized to return an rvalue reference instead, making the result movable, which would again be an optimization.
TL;DR: Use it to prevent incorrect usage of a function or for optimization.
I'm working on g++ and here I have tried to overload a function by just adding const to parameter. It works fine and when it runs, it calls the function without const
Is this behavior specified in the C++ standard?
What the reason it calls the function without const
void print(const std::string& str){std::cout << "const" << str << std::endl;}
void print(std::string& str){std::cout << str << std::endl;}
int main()
{
std::string temp = "hello";
print(temp);
return 0;
}
Reference bindings are an identity category §13.3.3.1.4) but since the latter is more cv-qualified, for §13.3.3.2, the non-const is preferred (sample code from the standard):
int f(const int &);
int f(int &);
int i;
int j = f(i); // calls f(int &)
That is standard behavior. Any other behavior would lead to crazy behavior. In particular, the non-const function would not be callable at all.
const is part of method signature. Overriding works only for methods with the same signature.
This behavior was made to avoid reverse situation when you use const method of base class to call not const method of child class.
The reason is this section in [over.ics.rank]/3 where this case is explicitly covered:
Standard conversion sequence S1 is a better conversion sequence than
standard conversion sequence S2 if […] — S1 and S2 are reference
bindings (8.5.3), and the types to which the references refer are the
same type except for top-level cv-qualifiers, and the type to which
the reference initialized by S2 refers is more cv-qualified than the
type to which the reference initialized by S1 refers.
S1 corresponds to the second overload and S2 to the first.
What the reason it calls the function without const
You always try to select the most specialized thing. That is the case in overload resolution just as it is in partial ordering of function templates. The second overload is more specialized than the first because the first can be called with arguments which the second cannot be called with - that is the basic reasoning behind this rule.
Overloading works by matching the types of the arguments, including the qualifiers. In your case temp has type std::string not const std::string. You have only initialised it with a literal constant, it is not itself constant.
Consider the following:
std::string temp( "hello" ) ;
print(temp); // hello
print( std::string("hello") ) ; // consthello
print( "hello" ) ; // consthello
print( static_cast<const std::string>(temp) ) ; // consthello
const std::string temp2( "hello" ) ;
print(temp2); // consthello
If you were to remove the non-const version, all three will call the remaining const overload. In this example, only the const version is in fact necessary (and preferred) since neither version modify the string object.
If on the other hand you removed the non-const version, there would be no function matching any but the first example above, and the build would fail. That is to say a non-const object can safely be passed as a const argument, but a const object cannot be passed as a non-const argument, because the function is not "promising" not to modify the object. You can force a const into a non-const argument by a const_cast as in:
const std::string temp2("hello") ;
print( const_cast<std::string&>(temp2) ) ; // hello
But if print() were to attempt to modify the object in this case the results are undefined, so consider the practice unsafe.
Making an argument const indicates intent, allows the compiler to issue a diagnostic if the code is attempts to modify the object or pass it via a non-const argument to some other function. It may also potentially provide the compiler with optimisation possibilities.
Because calling the function taking std::string const& requires two implicit conversions: one to std::string const, one to std::string const&; whereas calling the function taking std::string& requires merely one implicit conversion (to std::string&), so that one is preferred.