1

This code:

class foo
{
    int x;
public:
    foo(int x) : x(x) { }
    int get() const { return x; }
    //...
};

class bar
{
    int x;
public:
    bar(const foo& x) : x(x.get()) { }
    int get() const { return x; }
    bar& operator =(const foo& rhs) { x = rhs.get(); return *this; }
    //...
};

void func()
{
    foo f = 3;
    bar b = 3;
    b = 7;
    //...
}

errors out on the bar b = 3 line (g++ 4.7.1 with -fstd=gnu++11):

error: conversion from 'int' to non-scalar type 'bar' requested

However, I'm providing a bar constructor that takes a foo, and ints can be implicitly converted to foo as shown by the line preceding it. So, what is going wrong?

By the way, for several reasons it is undesirable to force conversion to foo using foo(3) because that would make my actual code ugly to use and read.

fincs
  • 207
  • 3
  • 8

2 Answers2

5

I'm providing a bar constructor that takes a foo, and ints can be implicitly converted to foo as shown by the line preceding it. So, what is going wrong?

Chained-conversion is not allowed in C++, which means the (chained) conversion cannot occur:

int -> foo -> bar  //not allowed (chained conversion)

even though the followings are given:

int -> foo  //given
foo -> bar  //given

So if you want int -> bar to work, then add another constructor taking int to class bar.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • I guess I should also add an `operator =(int)` overload so that the assignment is not ambiguous. – fincs Feb 16 '13 at 21:34
  • @fincs: If you add `bar(int)`, then you don't need to add `operator=(int)`. – Nawaz Feb 16 '13 at 21:35
  • Yes I do, the compiler complains: `error: ambiguous overload for \`operator=\` in \`b = 7\``. – fincs Feb 16 '13 at 21:36
  • @fincs: Oh, you have `operator=(foo const&)` which is causing ambigious. I didn't see this before. – Nawaz Feb 16 '13 at 21:37
  • @Nawaz: I'm actually trying to figure out why the assignment works while the copy-initialization doesn't. Am I missing something obvious? – Andy Prowl Feb 16 '13 at 21:37
  • @AndyProwl: `operator=` are not considered user-defined conversion function. So there is only one conversion taking place for assignment. Note that `b=7` is same as `b.operator=(7)` which is conceptually same as `b.some_function_taking_foo(7)`. – Nawaz Feb 16 '13 at 21:39
  • @Nawaz: Interesting. Do you happen to know where is this specified? 13.3.3.1/1 says: "An implicit conversion sequence is a sequence of conversions used to convert an argument in a function call to the type of the corresponding parameter of the function being called". – Andy Prowl Feb 16 '13 at 21:41
  • @AndyProwl: Search for *"converting constructor"* in the spec, and *user-defined conversion*. – Nawaz Feb 16 '13 at 21:42
  • 1
    @Nawaz: Ok, I got it. Here the target type of the conversion is `foo`, while in the copy-initialization the target type is `bar`. I somehow assumed `operator =` takes a `bar const&`, I should learn to read. – Andy Prowl Feb 16 '13 at 21:43
0

§ 13.3.3.1.2/1 about implicit user-defined conversion sequences mandates:

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. If the user-defined conversion is specified by a constructor (12.3.1), the initial standard conversion sequence converts the source type to the type required by the argument of the constructor. If the user-defined conversion is specified by a conversion function (12.3.2), the initial standard conversion sequence converts the source type to the implicit object parameter of the conversion function.

This means it is not possible to chain more than one user-defined conversion.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451