It compiles with /permissive but fails with /permissive-. What is not conforming and how to fix it?
Why it's fine in (2) but fails in (4)(3)?
If I remove operator long it also fine.
How to fix it without changing call site (3,4)?
#include <string>
struct my
{
std::string myVal;
my(std::string val): myVal(val) {}
operator std::string() { return myVal; };
operator long() { return std::stol(myVal); };
};
int main()
{
struct MyStruct
{
long n = my("1223"); // (1)
std::string s = my("ascas"); // (2)
} str;
str.s = my("ascas"); // (3)
str.n = my("1223"); // (4)
}
error message
error C2593: 'operator =' is ambiguous
xstring(2667): note: could be 'std::basic_string<...> &std::basic_string<...>::operator =(const _Elem)'
with
[
_Elem=char
]
xstring(2648): note: or 'std::basic_string<...> &std::basic_string<...>::operator =(const std::basic_string<...> &)'
xstring(2453): note: or 'std::basic_string<...> &std::basic_string<...>::operator =(std::basic_string<...> &&) noexcept(<expr>)'
Source1.cpp(17): note: while trying to match the argument list '(std::string, my)'
I suppose you meant it's fine in (2) but fails in (3)
Note that the #2 is initialization, which calls the constructor of std::string; the #3 is assignment, which calls the assignment operator of std::string. They're different things.
The invocation of assigment operator is ambiguous because the assignment operator of std::string has an overload taking char, which could be implicitly converted from long (which is a stardard conversion), then leads to ambiguity (with the assignment operators taking std::string, as the compiler complained). Both the implicit conversion sequence contain one user-defined conversion (from my to std::string or long), they have the same rank in onverload resolution.
The invocation of constructor is fine because it doesn't have such overload (taking char).
The problem is that in the case #2 there is used a constructor while in the case #3 there is used an assignment operator.
The assignment operator is overloaded like
basic_string& operator=(charT c);
But there is no constructor that accepts only one argument of the type charT
So for the case #2 there is used the user-defined conversion operator
operator std::string() { return myVal; };
and then the constructor
basic_string(basic_string&& str) noexcept;
In the case #3 there are two posiibilities.
The first one is to call the conversion operator
operator std::string() { return myVal; };
and then the assignment operator
basic_string& operator=(basic_string&& str)
And the second one is to call the conversion operator
operator long() { return std::stol(myVal); };
and then the assignment operator
basic_string& operator=(charT c);
It is interesting to note the following additional case.
If you will wrote
str.s = { my("ascas") };
then there will not be an ambiguity. The compiler will select the operator that accepts an std::initializer_list. That is it will select the assignment operator
basic_string& operator=(initializer_list<charT>);
In this case there will be used the conversion operator
operator long() { return std::stol(myVal); };
but as the string "ascas" can not be converted to the type long a runtime error will occur
terminate called after throwing an instance of 'std::invalid_argument'
what(): stol
Vlad from Moscow answer have a nice explanation. But there was no solution.
Here it is using SFINAE
template<typename T = long, typename = std::enable_if_t<
std::is_same_v<T, long> || std::is_same_v<T, int>>>
operator T() const
{
return l();
}
operator std::string() const
{
return s();
}
Related
#include <type_traits>
#include <iostream>
template<typename T>
struct Wrapper {
T value;
operator T&() & { std::cout << "call const ref" << std::endl; return this->value; }
operator const T&() const& { std::cout << "call const ref" << std::endl; return this->value; }
operator T&&() && { std::cout << "call move" << std::endl; return std::move(this->value); }
operator const T&() const&& = delete;
operator T&&() & = delete;
operator T&&() const& = delete;
};
class A {
public:
A& operator=(const A&) { std::cout << "use copy" << std::endl; return *this; }
A& operator=(A&&) { std::cout << "use move" << std::endl; return *this; }
};
int main() {
Wrapper<A> b;
A bb;
bb = std::move(b);
}
I compiled this code with gcc10.2, and get the following error
test.cc: In function ‘int main()’:
test.cc:24:21: error: ambiguous overload for ‘operator=’ (operand types are ‘A’ and ‘std::remove_reference<Wrapper<A>&>::type’ {aka ‘Wrapper<A>’})
24 | bb = std::move(b);
| ^
test.cc:17:12: note: candidate: ‘A& A::operator=(const A&)’
17 | A& operator=(const A&) { std::cout << "use copy" << std::endl; return *this; }
| ^~~~~~~~
test.cc:18:12: note: candidate: ‘A& A::operator=(A&&)’
18 | A& operator=(A&&) { std::cout << "use move" << std::endl; return *this; }
| ^~~~~~~~
But I tried the same code with clang in cppinsights.io, and compiled successfully.
link
So, what cause the difference between gcc and clang?
And how do I change the Wrapper to fix it?
https://godbolt.org/z/37dPqafGK
Quoted text is from the C++20 standard, but links are to N4861.
First let's minimize the given code. The results with GCC 11.2 and Clang 12.0.1 are as indicated in the question (Clang accepts, GCC rejects) if three of the conversion functions are deleted, leaving only:
operator const T&() const&; // not deleted
operator T&&() &&; // not deleted
operator const T&() const&& = delete;
From this we can infer that Clang only accepts the code because, in trying to form an implicit conversion sequence that would enable A& A::operator=(const A&) to be called, it selects the deleted Wrapper<A>::operator const T&() const&& to perform the conversion to const A&. Thus, forming the implicit conversion sequence fails, and A& A::operator=(const A&) is not viable, leaving only the other candidate.
The divergence between GCC and Clang appears to be related to CWG issue 2525. The issue is as follows: it appears that the intent of Note 1 to [over.ics.best.general]/2 ([over.ics.best]/2 in N4861) is that if an implicit conversion sequence involves a deleted function, the implicit conversion sequence is still considered to be formed, and only if the candidate that requires this implicit conversion sequence is selected, then the program is ill-formed since it requires a call to the deleted function. But the normative wording does not seem to reflect this intention.
GCC and Clang agree that the declaration const A& t = std::move(b); is ill-formed because it invokes the deleted function Wrapper<A>::operator const A&() const&&. Thus, Clang appears to be following the normative wording as it currently exists: since the initialization const A& t = std::move(b) would be ill-formed, it follows that std::move(b) is not implicitly convertible to const A& under [conv]/3, and thus the implicit conversion sequence required to make the copy assignment operator a viable candidate does not exist, and only the move assignment operator is viable according to Clang. But GCC is following what the note seems to be telling us: to ignore (at the overload resolution stage) the fact that one of the implicit conversion sequences contains a deleted function. This ultimately results in GCC being unable to decide whether to call the copy assignment operator or the move assignment operator.
It looks like the problem is that you have two overloads. There's the overload resolution of A::operator=, but there is also the overload resolution of the Wrapper<A> conversion operator in the conversion sequence. C++ cannot resolve multiple overloads simultaneously.
For a simpler case, imagine if A::operator= was overloaded for int and float, and Wrapper<A> defined both operator int and operator float.
This is of course only the case if there is an overload, i.e. there are multiple candidate conversion operators in Wrapper<A>. But I don't see a reason why clang should exclude 2 out of the 3 conversion operators.
I also don't seen an obvious fix. The problem itself looks ambiguous to me.
Is there a way to disable conversion operators? Marking them "= delete" messes up other things.
Consider the following code:
class Foo
{
public:
Foo() :mValue(0) {}
~Foo() = default;
Foo(int64_t v) { mValue = v; }
Foo(const Foo& src) = default;
bool operator==(const Foo& rhs) { return mValue == rhs.mValue; }
/* after commenting these lines the code will compile */
operator int() const = delete;
operator int64_t() const = delete;
private:
int64_t mValue;
};
int main()
{
Foo foo1(5);
Foo foo2(10);
bool b1 = (foo1 == foo2);
bool b2 = (foo1 == 5);
}
This won't compile because gcc complains that the == operator is ambiguous:
test.cc: In function ‘int main()’:
test.cc:25:21: error: ambiguous overload for ‘operator==’ (operand types are ‘Foo’ and ‘int’)
bool b2 = (foo1 == 5);
^
test.cc:25:21: note: candidates are:
test.cc:25:21: note: operator==(int, int) <built-in>
test.cc:25:21: note: operator==(int64_t {aka long int}, int) <built-in>
test.cc:10:10: note: bool Foo::operator==(const Foo&)
bool operator==(const Foo& rhs) { return mValue == rhs.mValue; }
^
However, after commenting the conversion operators, the code will compile and run nicely.
The first question is: why do the deleted conversion operators create an ambiguity for the == operator? I thought they should disable implicit Foo -> int conversions but they seem to affect int -> Foo conversions which does not sound logic to me.
Second one: is there a way to mark the conversion operators deleted? Yes, by not declaring them - but I'm looking for a way that anyone in the future will see that those conversions are disabled by design.
Any use of a deleted function is ill-formed (the program will not
compile).
If the function is overloaded, overload resolution takes place first,
and the program is only ill-formed if the deleted function was
selected.
In you case programm can't select conversion becouse you have 3 variant
int -> Foo
Foo -> int
Foo -> int64
Second question:
you can leave it as it is, but always use explicit conversion for int
bool b2 = (foo1 == Foo(5));
Here's what I think is the crux of the matter:
[dcl.fct.def.delete]:
A program that refers to a deleted function implicitly or explicitly, other than to declare it, is ill-formed.
...
A deleted function is implicitly an inline function ([dcl.inline]).
[class.member.lookup/4]:
If C contains a declaration of the name f, the declaration set
contains every declaration of f declared in C that satisfies the
requirements of the language construct in which the lookup occurs.
Even if you delete the function, you still declare it. And a declared function will participate in overload resolution. It's only when it's the resolved overload, that the compiler checks if it's deleted.
In your case, there is an obvious ambiguity when those function declarations are present.
Is it possible for assignment operator to be deduced as a special case of member function template?
For example, I have a class template with one bool parameter and want to implement assign operation regardless any particular value of the template argument.
#include <iostream>
template<bool sw>
struct A {
A() {
std::cout << __PRETTY_FUNCTION__ << '\n';
}
template<bool input_sw>
A & operator = (const A<input_sw> &a) {
std::cout << __PRETTY_FUNCTION__ << '\n';
return *this;
}
};
int main()
{
A<true> a;
A<true> b;
a = b;
}
In the code snippet above clang and gcc-compiled binaries print out nothing about assignment -- as far as I can tell default assignment is generated here despite possibility to deduce it from the template.
It is correct for them to not print anything out. What is happening is that there is an implicit copy assignment operator created for A<true> with this signature:
A<true>& operator=(const A<true>& );
Thus, when we do overload resolution, there are two viable candidates:
A<true>& operator=(const A<true>& ); // implicit copy assignment
A<true>& operator=(const A<input_sw>& ); // [ with input_sw = true ]
Both operators take the same argument (const A<true>&), so they are equivalently good candidates from this perspective. But non template functions are preferred to function template specializations, which makes the implicit copy assignment operator the best viable candidate.
Now, consider an alternative. What if you declared your operator template this way:
template<bool input_sw>
A & operator =(A<input_sw> &a) { ... }
That is, not const. This isn't good practice, and I'm presenting this solely for illustrative purposes. In this case, our two candidates for a=b are:
A<true>& operator=(const A<true>& ); // implicit copy assignment
A<true>& operator=(A<input_sw>& ); // [ with input_sw = true ]
Now the two candidates do not take the same argument. Our operator template takes a reference to non-const. In the case of two references, the one referring to the last cv-qualified type is preferred, which in this case would be the operator. Drop that const, and now your function is preferred, and you will see something printed.
C++ is the best.
I am studying converting constructors and conversion operators in C++.
What I've learned so far is that any non-explicit constructor that takes only one parameter (and any number of optional default arguments) represents an implicit class-type conversion to THAT class type, for example if a class defines a constructor that has one parameter of type int I can use an int wherever an object of that class type is required:
(assuming class_type has an overloaded += operator)
class_type a;
a+=5;
in this case 5 is implicitly converted (through the converting constructor) to class_typeand the overloaded operator is called.
Now, the (at least for me) tricky part: I know I can define a conversion operator as a member function :
operator int() {....};
that converts the object of class_type to the primitive int type, and I can use that conversion like:
class_type a;
a+5;
in this case I've read that the object is converted to an int through its conversion operator and then the buil-in sum operator is called.
But what if I defined an overloaded + operator to take two class_type objects as its arguments? something like
class_type operator+(const class_type&,const class_type &c);
how is the compiler supposed to know which one to call through function matching?
does the conversion to int only happens implicitly when only the built-in operator is defined?
thanks!
edit:
actually,I've tried to write some code to effectively try it out, it turned out that my compiler (g++) doesn't issue any ambiguous call error!
this is the class header (along with the non-memeber operator+ function declaration) :
#include <iostream>
class wrapper {
friend std::ostream &operator<<(std::ostream&,const wrapper&);
public:
wrapper()=default;
wrapper(int);
int get();
operator int() const;
wrapper operator+(int);
private:
int a=10;
};
std::ostream &operator<<(std::ostream&,const wrapper&);
and this is the main code:
#include "wrapper.h"
int main()
{
using namespace std;
wrapper w1;
wrapper w2(5);
cout<<w1<<" "<<w2<<endl;
w1+1;
}
now,I've defined a converting constructor from int to wrapper AND a conversion operator from class type to int(I've also overloaded the << output operator in order to print some results), but when the compiler evaluates the expression w1+1 it seems to be fine. How could it possibly be??
If you have for example the following class declaration that contains a conversion constructor and a conversion operator
struct A
{
A( int x ) : x( x ) {}
operator int() const { return x; }
int x;
};
const A operator +( const A &a1, const A &a2 )
{
return A( a1.x + a2.x );
}
then statement
a1 + a2;
where a1 and a2 are declared like for example
A a1( 10 );
A a2( 20 );
will be well-formed because there is no need to call a conversion function. The both operands match the parameter declarations of the operator +.
However if you will write for example
a1 + 20;
when the compiler issues an error because there is an ambiguity. The compiler can either apply conversion constructor A( int ) to convert the second operand to type A and call the operator defined for objects of type A. Or it can apply the conversion operator operator int to convert the first operand to type int and call the built-in operator + for objects of type int.
To avoid this ambiguity you could declare either the constructor or the operator (or the both) with function specifier explicit.
For example
explicit A( int x ) : x( x ) {}
or
explicit operator int() const { return x; }
In this case only one implicit conversion would exist and there was not an ambigiuty.
I would like to append the above description that sometimes some converion operators can be called implicitly even if they are declared with the function specifier explicit.
For example According to the C++ Standard (6.4 Selection statements)
...The value of a condition that is an expression is the value of the
expression, contextually converted to bool for statements other
than switch;
and (5.16 Conditional operator)
1 Conditional expressions group right-to-left. The first expression is
contextually converted to bool (Clause 4).
So for example if the above class has the following conversion operator declared with the function specifier explicit
explicit operator bool() const { return x != 0; }
nevertheless it will be called implicitly for example in the following statement
A a( 10 );
std::cout << ( a ? "true" : "false" ) << std::endl;
Here a will be converted to an object of type bool in the conditional operator.
EDIT: After you updated your question this expression
w1+1;
is an exact match for operator
wrapper operator+(int);
Neither conversion are required. So the code compiles successfully.
This is something you can easily try and see what the compiler does:
#include <iostream>
struct ABC {
int v;
ABC(int x) : v(x) { }
operator int() const { return v; }
void operator +=(ABC const &that) {
v += that.v;
}
};
ABC operator+(ABC const &lhs, ABC const &rhs) {
return { lhs.v + rhs.v };
}
int main() {
ABC a(5);
std::cout << a + 1 << '\n';
a += 10;
std::cout << a << '\n';
}
what if I defined an overloaded + operator to take two class_type objects as its arguments?
GCC
error: ambiguous overload for 'operator+' (operand types are 'ABC' and 'int')
The compiler sees two candidates: operator+(int, int) <built-in> and ABC operator+(const ABC&, const ABC&). This means it could implicitly convert not only the 5 in a + 5 to a but also the a to int. Post these conversions both operator+ functions become potential matches.
How is the compiler supposed to know which one to call through function matching?
It doesn't know hence the error.
does the conversion to int only happens implicitly when only the built-in operator is defined?
Yes, otherwise it doesn't automatically convert class_type to int. However, int to class_type would happen implicitly unless you make class_type's constructor explicit:
explicit ABC(int x) : v(x) { }
If you've access to C++11, then you also make the conversion function explicit:
explicit operator int() const { return v; }
I have these codes:
class Type2 {
public:
Type2(const Type1 & type);
Type2(int);
const Type2 & operator=(const Type2 & type2);
//....
};
...
Type1 t1(13);
Type2 t2(4);
t2=t1;
As I understood, the 1-argument constructors of Type2 each without an explicit keyword should mean any Type1 objects or int values can be implicitly conveted to Type2 objects.
But in the last line t2=t1;, MS Visual Studio gives me this compile error:
....error C2679: binary '=' : no operator
found which takes a right-hand operand
of type 'Type1' (or there is no
acceptable conversion)....
Seems like MS Visual Studio insisting t2=t1; must match an assignment operator with lhs=Type2 and rhs=Type1. Why can't it implicitly cast rhs to t2 and then do the copying with the Type2=Type2 operator?
I've found the answer. Because my Type1 got a conversion operator
class Type1 {
public:
Type1 (int );
operator Type2() ;//casting to Type2
....
};
This is something called "dual-direction implicit conversion"
This code:
#include <iostream>
using ::std::cerr;
class Barney;
class Fred {
public:
Fred() { }
Fred(const Barney &b) { cerr << "Using conversion constructor.\n"; }
};
class Barney {
public:
Barney() { }
operator Fred() const { cerr << "Using conversion operator.\n"; return Fred(); }
};
int main(int argc, const char *argv[])
{
const Barney b;
Fred f;
f = b;
return 0;
}
generates this error in gcc 4.6:
g++ -O3 -Wall fred.cpp -o a.out
fred.cpp: In function ‘int main(int, const char**)’:
fred.cpp:23:8: error: conversion from ‘const Barney’ to ‘const Fred’ is ambiguous
fred.cpp:21:17: note: candidates are:
fred.cpp:16:4: note: Barney::operator Fred() const
fred.cpp:10:4: note: Fred::Fred(const Barney&)
fred.cpp:7:7: error: initializing argument 1 of ‘Fred& Fred::operator=(const Fred&)’
Compilation exited abnormally with code 1 at Sun Jun 19 04:13:53
Now, if I remove the const after operator Fred(), it then compiles, and uses the conversion constructor. If I also remove the const from the declaration of b in main, it then prefers the conversion operator.
This all fits the overload resolution rules. And gcc generates the appropriate ambiguity error when it can't pick between the conversion operator and the conversion contructor.
I notice that in the examples you gave, the conversion operator is missing a const. This means that there will never be a case in which using the conversion operator or the converting constructor is ambiguous.