zhangwfjh
zhangwfjh

Reputation: 123

Implicit conversion of template function

I have a class A in which most operations are implemented. Also, I have another class B which contains only a member of A. And I wish operations in A can be directly applied to B. So I define a conversion operation. But the compiler complains "error: no matching function for call to 'foo'". What's wrong with implicit conversion and how to implement this? Thanks.

Edit: What if I add an operator overloading to A and want B to use it directly?

template <typename T> struct B;

template <typename T>
struct A {
  A(const B<T>& b) {}    // Conversion from B to A, way 1
  friend void foo(const A&);
  // Addition
  friend A operator+(const A&, const A&);
};

template <typename T>
void foo(A<T>& a) {}

// Addition
template <typename T>
A<T> operator+(const A<T>& a1, const A<T>& a2) { return A<T>(); }

template <typename T>
struct B {
    B() {}
    A<T> a;
    operator A<T>() { return a; }   // Conversion from B to A, way 2
    // Addition
    B(const A<T>& a) : a(a) {}
};

int main()
{
    B<int> b;
    foo(b);
    auto bb = b+b;
}

Upvotes: 0

Views: 134

Answers (2)

R Sahu
R Sahu

Reputation: 206567

There are two major problems in your posted code.

  1. The compiler does not convert B<int> to A<int> to deduce the template parameters for foo to be int. You'll have to help the compiler. Use:

    foo<int>(B<int>());
    

    or

    foo(static_cast<A<int>>(B<int>()));
    

    That will solve only half the problem.

  2. The conversion function, no matter which one is used by the compiler, results in a temporary object. A temporary object cannot bind to a A<int>&. You'll have to use

    template <typename T> void foo(A<T>) {}
    

    or

    template <typename T> void foo(A<T> const&) {}
    

Also, the friend declaration in A is not right. It declares a non-template function foo to be the friend of A<T>. If you want foo<T> to be a friend of A<T>, you'll have to change your code a little bit.

// Declare the class template A first.
template <typename T> class A;

// Declare the funtion template foo next.
template <typename T> void foo(A<T>);

// Declare foo<T> to be friend of A<T> in the definition of A.

template <typename T>
struct A {
  ...
  friend void foo<>(A);
};

Here's a complete program that builds successfully for me.

template <typename T> struct B;
template <typename T> struct A;
template <typename T> void foo(A<T> a);

template <typename T>
struct A {
   A(const B<T>& b) : x{b.a.x} {}    // Conversion from B to A, way 1
   A(T x) : x{x} {}
   T x;
   friend void foo<>(A);
};

template <typename T>
void foo(A<T> a) {}

template <typename T>
struct B {
   B() : a{0} {}
   A<T> a;

   // This is not necessary for conversion of B<T> to A<T>
   // operator A<T>() { return a; }   // Conversion from B to A, way 2
};

int main()
{
   foo<int>(B<int>());
   foo(static_cast<A<int>>(B<int>()));
}

Upvotes: 4

Remy Lebeau
Remy Lebeau

Reputation: 595711

foo() takes a non-const reference as input. B's conversion operator returns a temporary A object, and a temp cannot be bound to a non-const reference parameter. That is why your call to foo() fails to compile.

If you were to change the input parameter to take a const reference instead, it can bind to a temp object. But, A has a copy constructor that takes a const reference to a B object as input, so your call to foo() might still be ambiguous. After constructing the temp B object, should the compiler call the A copy constructor with the B as input, or should it call the B conversion operator that returns an A? I'm not sure what the standard says about that, or how compilers implement that.

Upvotes: 0

Related Questions