C++ implicit conversions
Asked Answered
B

4

28

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.

Brockway answered 15/5, 2009 at 7:52 Comment(4)
I'm sorry i was all wrong in the comment to your answer. I read "Overload resolution is used to select the user-defined conversion to be invoked.", and i said to myself 'well, then it will resolve to A(string const&) and pass it "one"', but i totally failed to think of what 13.3.3.1.2 says: "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." But "one" -> string would not be a standard conversion sequence, but require another user defined conversion sequence!Kedgeree
Just so long as I'm not losing my marbles :-)Brockway
Great question! The discussion implies that std::string is not part of the language and conversions to/from it are "user-defined". At least that's my understanding, correct me if I'm wrong. It would be nice if the question were more explicit about this. Exact status of std::string may be crystal-clear to old C++ hands, but is not so easy to realize for people who came to the language in this century.Bowie
@just Correct - std::string is just a library class - you could write one identical to it yourself, so the language does not specify any special processing for it.Brockway
S
13

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.

Shedevil answered 15/5, 2009 at 9:50 Comment(1)
I feel compelled to share why I enjoy using implicit conversions in a specific scenario: namely, when I want to couple a variable of a simple type with a class that performs related functions on the variable. In addition to the implicit conversion, I provide a function in the class that returns the value, in the same way that std::string provides c_str(). Then you can have the extra class functionality where needed, and you can go extremely lightweight elsewhere, all while keeping the related functionality together.Moriahmoriarty
S
9

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.

Swen answered 15/5, 2009 at 7:56 Comment(0)
S
9

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.

Smithery answered 15/5, 2009 at 12:26 Comment(0)
R
7

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.

Recovery answered 15/5, 2009 at 8:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.