4

I have the following code, which constructs one object t2 using a explicit conversion constructor, which performs an implicit conversion of t1. This is expected, and is described in The C++ Programming Language, in section 11.4.1 of the 3rd edition.

#include <iostream>
#include <string>
using namespace std;

class test1 {
public:
    test1() {}
    operator string() {
        cout << "test1 string conversion operator called" << endl;
        return string();
    }
};
class test2 {
public:
    test2() {}
    test2(string s) {
        cout << "test2 string conversion constructor called" << endl;
    }
};

int main() {
    test1 t1;
    test2 t2(t1);
    return 0;
}

And as you would expect:

> clang++ --version
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.2
Thread model: posix
> clang++ -std=c++11 test.cc
> ./a.out
test1 string conversion operator called
test2 string conversion constructor called

However, when changing t2's construction to initialization syntax:

test1 t1;
test2 t2 = t1;
return 0;

Clang outputs the following:

test.cc:23:15: error: no viable conversion from 'test1' to 'test2'
        test2 t2 = t1;
              ^    ~~
test.cc:13:11: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'test1' to 'const test2 &' for 1st argument
    class test2 {
          ^
test.cc:13:11: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'test1' to 'test2 &&' for 1st argument
    class test2 {
          ^
test.cc:16:9: note: candidate constructor not viable: no known conversion from 'test1' to 'string' (aka 'basic_string<char, char_traits<char>, allocator<char> >') for 1st argument
        test2(string s) {
        ^
test.cc:8:9: note: candidate function
        operator string() {
        ^
1 error generated.

I don't know if initialization is supposed to be able to perform an implicit conversion like this, but the error message seems very very wrong. no known conversion from 'test1' to 'string', yet it even shows candidate function operator string() {

What gives? And what does the C++ standard say about implicit conversions in initialization constructors? I assume that this should be counted as two implicit conversions, and thus not allowed, but the compiler output doesn't suggest that at all.

peter
  • 43
  • 2
  • 4

1 Answers1

5

First off, it's wrong to call test2::test2(string) an "explicit conversion constructor". It'll be used in implicit conversions (mark it explicit if you don't want that).

Anyway, clang's error message is spot on and it almost perfectly explains what's going on.
This:

test2 t2(t1);

is called direct initialization. All constructors for test2 are candidates and additionaly, compiler can run an implicit conversions sequence to match the arguments. It finds test1::operator string and test2::test(string) and all is well.

This:

test2 t2 = t1;

is called copy initialization. The expression on the right of = needs to be converted to test2 and then either a copy- or move-constructor will be called to construct the object (in theory at least, it can later be elided as an optimization, but it must be accessible nonetheless).

jrok
  • 54,456
  • 9
  • 109
  • 141
  • Why does the notes say there is no known conversion though? The bold text is what I find most confusing. Like, it says in the note that the string conversion constructor is not viable **because there is no way to convert test1 to string**. But there is. – peter Nov 15 '13 at 19:32
  • So, it can't convert t1 to t2 without using two implicit conversions, the implicit test1->string and the implicit conversion from copy initialization. But the compiler output suggests it tried to double convert and couldn't find the test1->string conversion. Is it just wrong? – peter Nov 15 '13 at 19:44
  • Hm, on second reading, it is indeed confusing and looks wrong. I'm no authority to critique clang developers, though and also can't say why they chose this kind of error here. – jrok Nov 15 '13 at 19:49
  • Okay. That's good to know. I was thinking it was correct and I was not understanding something. – peter Nov 15 '13 at 19:54
  • 1
    @peter Note that there *can* be two implicit conversion, it's just that only one can be user defined. In `test2 t2 = t1` there is one user defined conversion (via `test2::test(string)`). It there were a standard conversion sequence needed, it would be ok. But it's not, it needs `operator string`. So the compiler does see it, but it's not viable. Maybe that's the reason for the error message. – jrok Nov 15 '13 at 20:20