C++ implicit conversions - c++

Several comments on a recent answer of mine, What other useful casts can be used in C++, suggest that my understanding of C++ conversions is faulty. Just to clarify the issue, consider the following code:
#include <string>
struct A {
A( const std::string & s ) {}
};
void func( const A & a ) {
}
int main() {
func( "one" ); // error
func( A("two") ); // ok
func( std::string("three") ); // ok
}
My assertion was that the the first function call is an error, becauuse there is no conversion from a const char * to an A. There is a conversion from a string to an A, but using this would involve more than one conversion. My understanding is that this is not allowed, and this seems to be confirmed by g++ 4.4.0 & Comeau compilers. With Comeau, I get the following error:
"ComeauTest.c", line 11: error: no suitable constructor exists
to convert from "const char [4]" to "A"
func( "one" ); // error
If you can point out, where I am wrong, either here or in the original answer, preferably with reference to the C++ Standard, please do so.
And the answer from the C++ standard seems to be:
At most one user-defined conversion
(constructor or conversion function)
is implicitly applied to a single value.
Thanks to Abhay for providing the quote.

I think the answer from sharptooth is precise. The C++ Standard (SC22-N-4411.pdf) section 12.3.4 titled 'Conversions' makes it clear that only one implicit user-defined conversion is allowed.
1 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 (Clause
4), for
initialization (8.5), and for explicit type conversions (5.4,
5.2.9).
2 User-defined conversions are applied only where they are
unambiguous (10.2, 12.3.2).
Conversions obey the
access control rules (Clause 11). Access control is applied after
ambiguity resolution (3.4).
3 [ Note: See 13.3 for a discussion of the use of conversions
in function calls as well as examples
below. —end
note ]
4 At most one user-defined conversion (constructor or conversion
function) is implicitly applied to a
single
value.

That's true, only one implicit conversion is allowed.
Two conversions in a row may be performed with a combination of a conversion operator and a parameterized constructor but this causes a C4927 warning - "illegal conversion; more than one user-defined conversion has been implicitly applied" - in VC++ for a reason.

As the consensus seems to be already: yes you're right.
But as this question / answers will probably become the point of reference for C++ implicit conversions on stackoverflow I'd like to add that for template arguments the rules are different.
No implicit conversions are allowed for arguments that are used for template argument deduction. This might seem pretty obvious but nevertheless can lead to subtle weirdness.
Case in point, std::string addition operators
std::string s;
s += 67; // (1)
s = s + 67; // (2)
(1) compiles and works fine, operator+= is a member function, the template character parameter is already deduced by instantiating std::string for s (to char). So implicit conversions are allowed (int -> char), results in s containing the char equivalent of 67, e.g. in ASCII this would become 'C'
(2) gives a compiler error as operator+ is declared as a free function and here the template character argument is used in deduction.

The C++ Programming Language (4th. ed.) (section 18.4.3) says that
only one level of user-defined
implicit conversion is legal
That "user-defined" part makes it sound like multiple implicit conversions may be allowed if some are between native types.

Related

C++ primer 5 ed. Overloaded Functions and User-Defined Conversion

Hello I am at chapter 14 in C++ primer 5 ed. I've understood anything from previous chapters. It is really a very good book. however when I've reached this, it is a bit ambiguous for me:
"In a call to an overloaded function, if two (or more) user-defined conversions provide a viable match, the conversions are considered equally good. The rank of any standard conversions that might or might not be required is not considered. Whether a built-in conversion is also needed is considered only if the overload set can be
matched using the same conversion function.
For example, our call to manip would be ambiguous even if one of the classes defined a constructor that required a standard conversion for the argument:
struct E {
E(double);
// other members
};
void manip2(const C&);
void manip2(const E&);
// error ambiguous: two different user defined conversions could be used
manip2(10); // manip2(C(10) or
manip2(E(double(10)))
In this case, C has a conversion from int and E has a conversion from double. For the call manip2(10), both manip2 functions are viable:
• manip2(const C&) is viable because C has a converting constructor that takes an int. That constructor is an exact match for the argument.
• manip2(const E&) is viable because E has a converting constructor that takes a double and we can use a standard conversion to convert the int argument in order to use that converting constructor.
Because calls to the overloaded functions require different user-defined conversions from one another, this call is ambiguous. In particular, even though one of the calls requires a standard conversion and the other is an exact match, the compiler will still flag this call as an error.
Note In a call to an overloaded function, the rank of an additional standard conversion (if any) matters only if the viable functions require the same user-defined conversion. If different user-defined conversions are needed, then the call is ambiguous."
Can someone explain the last paragraph with an example? Thank you!

Overload resolution with an empty brace initializer: pointer or reference? [duplicate]

This question already has answers here:
Overload resolution: assignment of empty braces
(2 answers)
Closed 5 years ago.
I ran into a real-life WTF moment when I discovered that the code below outputs "pointer".
#include <iostream>
#include <utility>
template<typename T>
struct bla
{
static void f(const T*) { std::cout << "pointer\n"; }
static void f(const T&) { std::cout << "reference\n"; }
};
int main()
{
bla<std::pair<int,int>>::f({});
}
Changing the std::pair<int,int> template argument to an int or any other primitive type, gives the (for me at least) expected "ambiguous overload" error. It seems that builtin types are special here, because any user-defined type (aggregate, non-trivial, with defaulted constructor, etc...) all lead to the pointer overload being called. I believe the template is not necessary to reproduce it, it just makes it simple to try out different types.
Personally, I don't think that is logical and I would expect the ambiguous overload error in all cases, regardless of the template argument. GCC and Clang (and I believe MSVC) all disagree with me, across C++11/14/1z. Note I am fully aware of the bad API these two overloads present, and I would never write something like this, I promise.
So the question becomes: what is going on?
Oh, this is nasty.
Per [over.ics.list]p4 and p7:
4 Otherwise, if the parameter is a non-aggregate class X and overload resolution per 13.3.1.7 chooses a single best constructor of X to perform the initialization of an object of type X from the argument initializer list, the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion sequence an identity conversion. [...]
[...]
6 Otherwise, if the parameter is a reference, see 13.3.3.1.4. [Note: The rules in this section will apply for initializing the underlying temporary for the reference. -- end note] [...]
[...]
7 Otherwise, if the parameter type is not a class:
[...]
(7.2) -- if the initializer list has no elements, the implicit conversion sequence is the identity conversion. [...]
The construction of a const std::pair<int,int> temporary from {} is considered a user-defined conversion. The construction of a const std::pair<int,int> * prvalue, or a const int * prvalue, or a const int temporary object are all considered standard conversions.
Standard conversions are preferred over user-defined conversions.
Your own find of CWG issue 1536 is relevant, but mostly for language lawyers. It's a gap in the wording, where the standard doesn't really say what happens for initialisation of a reference parameter from {}, since {} is not an expression. It's not what makes the one call ambiguous and the other not though, and implementations are managing to apply common sense here.

Converting a stream to bool doesn't work on another compiler

Why with libstdc++ this works but with libc++ it fails? On gcc it also works:
bool b = std::cin;
You should add the language standard and compiler you compile with.
Until C++11, std::basic_ios had operator void*, since C++11 it has explicit operator bool instead.
The second one is explicit, meaning an implicit conversion like in your example cannot use it.
libstdc++ from the GNU project still unconditionally contains the pre-C++ conversion (Version 4.9.1):
operator void*() const
{ return this->fail() ? 0 : const_cast<basic_ios*>(this); }
The bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56193 is RESOLVED-FIXED since 2014-09-24, so the next release should be corrected.
According to the C++ Standard (13.3.1.5 Initialization by conversion function, p.#1)
The conversion functions of S and its base classes are considered.
Those non-explicit conversion functions that are not hidden within S
and yield type T or a type that can be converted to type T via a
standard conversion sequence (13.3.3.1.1) are candidate functions. For
direct-initialization, those explicit conversion functions that are
not hidden within S and yield type T or a type that can be converted
to type T with a qualification conversion (4.4) are also candidate
functions.
Class std::basic_ios has explicit conversion function operator bool. As
this declaration
bool b = std::cin;
does not use the direct initialization (there is the copy initialization) then it seems it is a bug of the compiler, that is the declaration shall not be compiled.

Different casting operators used by different compilers

The following C++ program compiles without warnings in all compilers I have tried (gcc 4.6.3, llvm 3.0, icc 13.1.1, SolarisStudio 12.1/12.3):
struct CClass
{
template<class T>
operator T() const { return 1; }
operator int() const { return 2; }
};
int main(void)
{
CClass x;
return static_cast<char>(x);
}
However, all but the SolarisStudio compilers return 2, SolarisStudio (either version) returns 1, which I would consider the most logical result.
Using return x.operator char(); results in all compilers returning 1.
Obviously, since figuring this out, I have been using the latter notation. However, I would like to know which of compilers is correct and why. (One would think that majority rules, but this still doesn't explain the why.)
This question seems to be related to the SO questions here, here, and here, but these "only" give solutions to problems, no explanations (that I was able to apply to my particular problem anyway).
Note that adding an additional overloaded casting operator, say operator float() const { return 3; } results in all compilers except SolarisStudio complaining about ambiguity.
The first (template) overload should be picked.
Paragraph 13.3.3/1 of the C++11 Standard specifies:
[...] a viable function F1 is defined to be a better function than another viable function
F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
— the context is an initialization by user-defined conversion (see 8.5, 13.3.1.5, and 13.3.1.6) and the
standard conversion sequence from the return type of F1 to the destination type (i.e., the type of the
entity being initialized) is a better conversion sequence than the standard conversion sequence from
the return type of F2 to the destination type. [ Example:
struct A {
A();
operator int();
operator double();
} a;
int i = a; // a.operator int() followed by no conversion
// is better than a.operator double() followed by
// a conversion to int
float x = a; // ambiguous: both possibilities require conversions,
// and neither is better than the other
—end example ] or, if not that,
— F1 is a non-template function and F2 is a function template specialization, or, if not that,
[...]
As you can see the, fact that the first conversion operator is a template only becomes relevant when the standard conversion sequence from its return type (char, in this case) to the destination type (char, in this case) is not better than the standard conversion sequence from the return type of the non-template overload (int, in this case) to the destination type (char, in this case).
However, a standard conversion from char to char is an Exact Match, while a standard conversion from int to char is not. Therefore, the third item of § 13.3.3/1 does not apply, and the second item does.
This means that the first (template) overload should be picked.
The first is an exact match, the second requires a conversion. Exact matches have priority over conversions.
Those other questions you linked are mostly unrelated to yours.
Some advice: don't use template conversion operators. Name it convert_to instead.

Non-const copy constructor and implicit conversions on return value

Consider the following C++ code:
struct B { };
struct A
{
A(int);
A(A&); // missing const is intentional
A(B);
operator B();
};
A f()
{
// return A(1); // compiles fine
return 1; // doesn't compile
}
This compiles fine on MSVC++ 2010 (in fact, on MSVC it even works if I remove B altogether). It doesn't on GCC 4.6.0:
conv.cpp: In function ‘A f()’:
conv.cpp:13:9: error: no matching function for call to ‘A::A(A)’
conv.cpp:13:9: note: candidates are:
conv.cpp:6:2: note: A::A(B)
conv.cpp:6:2: note: no known conversion for argument 1 from ‘A’ to ‘B’
conv.cpp:5:2: note: A::A(A&)
conv.cpp:5:2: note: no known conversion for argument 1 from ‘A’ to ‘A&’
conv.cpp:4:2: note: A::A(int)
conv.cpp:4:2: note: no known conversion for argument 1 from ‘A’ to ‘int’
What's confusing me is the message no known conversion for argument 1 from ‘A’ to ‘B’. How can this be true considering that A::operator B() is very well defined?
Because you cannot do more than one implicit conversion. You would have to go A::A(A::A(int)::operator B()) to make that work, and that's way too many steps for the compiler to figure out on it's own.
I don't think that "too many steps to figure on its own" as DeadMG pointed out is the reason. I've had constructs with 3-4 conversions, and the compiler always figured them out just fine.
I believe the problem is rather that the compiler is not allowed to convert a const reference to a non-constreference on its own behalf (it is only allowed to do that when you explicitly tell it with a cast).
And since the reference to the temporary object that is passed to the copy constructor is const, but the copy constructor is not, it doesn't find a suitable function.
EDIT: I didn't find any "real" code (see comments below) but constructed a multi-zigzag-convert example that actually compiles without errors under gcc 4.5. Note that this compiles just fine with -Wall -Wextra too, which frankly surprises me.
struct B
{
signed int v;
B(unsigned short in) : v(in){}
};
struct C
{
char v;
C(int in) : v(in){}
};
struct A
{
int v;
A(B const& in) : v(in.v){}
operator C() { return C(*this); }
};
enum X{ x = 1 };
int main()
{
C c = A(x);
return 0;
}
The error is quite clear on the list of candidates that were rejected. The problem is that implicit conversion sequences involving a user defined conversion in the C++ language are limited to a single user defined conversion:
§13.3.3.1.2 [over.ics.user]/1 A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-defined conversion (12.3) followed by a second standard conversion sequence.
The standard conversion sequences are defined in §4[conv]:
[...] A standard conversion sequence is a sequence of standard conversions in the following 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 conversion from the following set: integral promotions, floating point promotion, integral conversions, floating point conversions, floating-integral conversions, pointer conversions, pointer to member conversions, and boolean conversions.
Zero or one qualification conversion.
The problem is that your code cannot get from point a) int rvalue to point b) B by applying a single user defined conversion.
In particular, all conversion sequences that are available start with a user defined conversion (implicit constructor A(int)) that yield an A rvalue. From there, the rvalue cannot be bound to a non-const reference to call A::A( A& ), so that path is discarded. All the other paths require a second user defined conversion that is not allowed, and in fact the only other path that would get us to point b) requires two other user defined conversions for a total of 3.
The error lists all the potential candidates to be used, and why they cannot be used. It lists the conversion from B because its one of the constructors, but it doesn't know how to use it in this case, so it doesn't.