Wqh
Wqh

Reputation: 75

Why the implicit conversion doesn't work in g++

The code bellow can compile in Visual Studio, but is failed in gcc.

template<typename Isth>
class A
{
public:
    A(const boost::shared_ptr<Isth>& obj) {...} 
...
};

In class B's method:

A<Isth1> B::method1
{
    boost::shared_ptr<Isth2> myPtr = boost::make_shared<Isth2>();
  //Isth2 is derived from Isth1;
    ...
    return myPtr;
}

In gcc, I got an error "could not convert 'myPtr' from 'boost::shared_ptr' to 'A'" I think A's constructor should be called when B::method1 return.

Thanks in advance!

Upvotes: 1

Views: 155

Answers (3)

AlexStepanov
AlexStepanov

Reputation: 481

You're trying to do here multiple implicit conversions: boost::shared_ptr<Isth2> -> boost::shared_ptr<Isth1> -> A<Isth1>

As C++ standard says in the chapter 12.3 Conversions:

  1. At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.

This fixes the compilation:

A<Isth1> B::method1()
{
    boost::shared_ptr<Isth2> myPtr = boost::make_shared<Isth2>();
  //Isth2 is derived from Isth1;
    ...
    return A<Isth1>(myPtr);
}

Updated:

If you dont want to modify the return value, you could modify the constructor in your A class:

template <class Isth>
class A
{
    ...
    template <class T> A(const boost::shared_ptr<T>&) { ... }
    ...
};

Upvotes: 1

Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507005

The problem was highlighted by others - shared_ptr<Isth2> can only be converted to the constructor parameter of A<Isth1> if it wouldn't require a user defined conversion. But since it does, that constructor cannot be used.

Again uniform initialization helps to make this work

A<Isth1> B::method1()
{
    boost::shared_ptr<Isth2> myPtr = boost::make_shared<Isth2>();
  //Isth2 is derived from Isth1;
    ...
    return { myPtr };
}

For uniform initialization of a return value, user defined conversions for the constructor parameter is allowed. If you want to only change class A, you probably want to write some SFINAE

template<typename T, typename std::enable_if<
   std::is_base_of<Isth, T>::value, bool>::type = true>
A(const boost::shared_ptr<T>& obj) {...} 

You are basically stating "I am implicitly converting from any shared ptr that points at an object derived from Isth".

Upvotes: 5

SergeyA
SergeyA

Reputation: 62583

I am surprised Visual Studio compiles it. How do you expect to return a shared_ptr instead of an actual object? There is no way shared_ptr<X> can be converted to X.

OK, some clarifications are in order. That sort of conversion would have require to custom-conversion - one from shared_ptr<Isth2> to shared_ptr<Isth>, and another from shared_ptr<Isth> to A. Standard explicitly says that only one custom conversion is allowed. GCC is correct.

As to why Visual Studio is converting it, I am not sure. It either eagerly performs double conversion or it does not treat shared_ptr conversions as a custom conversion. Both options are wrong in my view.

Upvotes: 1

Related Questions