DMaster
DMaster

Reputation: 641

Issue when use two type-cast operators in template class

I define a template class in which, I define two type-cast operator

template <class base_t>
struct subclass {

  base_t base;

  //any function which defined for 'base_t' can be used with 'subclass<base_t>'
  operator base_t&() {
    return base;
  }

  //I want 'subclass<base_t>' can be converted to any class which 'base_t' can
  //I want this operator is called only if 'target_t' is not 'base_t'
  template <class target_t>
  operator target_t&() {
    return (target_t)base;
  }

};

The class looks fine, but there're a problem when I try to convert subclass<int> to int. For instance:

typedef subclass<int> foo_t;
foo_t foo = {1234};
cout << foo << endl; //Error here: ambiguous; This line is compiled only if I delete 'template<class target_t> operator target_t&()'

Please tell me how

EDIT

Everything work fine if I don't define the template. However, if I define a subclass< subclass<int> >, I can convert it to subclass<int> but I can't convert it to int

typedef subclass<int> level1;
typedef subclass<level1> level2;
level1 a = {1234};
level2 b = {a};
cout << a << endl;
cout << b << endl; //Error! Because the compiler doesn't provide a type-cast from level2 to int

Upvotes: 1

Views: 112

Answers (2)

Barry
Barry

Reputation: 302757

What you're trying to do makes little sense.

We have subclass<int>. It is convertible to int&, but also to a lot of other reference types. char&. bool&. double&. The ambiguity arises from the fact that all the various overloads for operator<< that take any non-template argument are viable overload candidates with equivalent conversion sequences. And for good measure, you probably don't want to do any of those conversions anyway.

What you want is just the base& operator:

template <class base_t>
struct subclass {
    base_t base;

    operator base_t&();
};

That's really all you need. This gives you the reference implicitly to the type you're storing, and you can already take base-class references to it too:

subclass<Derived> obj;
Base& base = obj; // totally OK with just that one operator

I don't see what problem the template operator T& solves.

However, when I define, subclass < subclass<int> > and it can converted to subclass<int> but can't be converted to int

First of all, subclass<subclass<int>> can be converted to subclass<int>&, not subclass<int>. That said, if we want that to work, we can add a conversion operator specifically for that case. First, add a type trait to find what the most nested type is:

template <class base_t>
struct subclass;

template <typename T>
struct nested { using type = T; };

template <typename T>
struct nested<subclass<T>> : nested<T> { };

And then define a template operator conversion for the nested case:

template <typename T, 
          typename = std::enable_if_t<std::is_same<T, typename nested<base_t>::type>::value>>
operator T&() {
    return base;
}

We make it a template so that there isn't an ambiguous overload in the case where we're just one subclass deep. And with that, both of these work:

subclass<subclass<subclass<int>>> s{{{4}}};
int& i = s;

subclass<int> s2{5};
int& i2 = s2;

Upvotes: 2

KABoissonneault
KABoissonneault

Reputation: 2369

You can use a bit of SFINAE techniques to make this work

template <class target_t, class = std::enable_if< !std::is_same<target_t, base_t>::value && std::is_convertible<base_t, target_t>::value> >
operator target_t&() {
    return (target_t)base;
}

Not the best syntax in the world, but that's how C++ currently works until Concepts Lite are standardized

Upvotes: 1

Related Questions