BryFry
BryFry

Reputation: 131

Guiding declaration for a templated conversion operator

Consider somethign like the following:

template <typename T> struct Foo;

template <typename S> struct Bar
{
  template <typename T> operator Foo<T>();

  // ...
};

class Baz
{
  Baz(Foo<int>&);
};

int main()
{
  Bar<float> bar;
  Baz        baz(bar); // Won't work
}

I would like to use a templated operator to specify a series of possible conversions in order to avoid duplication of code/copy and paste. However, of course, now a series of conversions as on the commented line of code won't work, because in C++ the compiler reasonable won't consider all forms of the templated operators, as that would be intractable.

If I instead choose cut and paste, and define Bar as follows:

template <typename S> struct Bar
{
  operator Foo<int>();
  operator Foo<float>();
  // many more

  // ...
};

Now the chained conversion can be determined and found. What I would like is to be able to have my cake and eat it to, in particular define the operator as a template, but also provide a series of guiding declarations which can be used as if the conversion operators were defined as non-templates:

template <typename S> struct Bar
{
  template <typename T> operator Foo<T>();

  template operator Foo<int>(); // Not valid C++
  template operator Foo<float>(); // Not valid C++
  // ...
};

Is there a way to accomplish this in C++11/C++14? Does anyone have an suggestions for structuring the code which would allow be to not have replications of the definition of the conversion operator, but still have a finite set of the instantiations of the conversion operator be used as if they were individually defined?

Upvotes: 1

Views: 71

Answers (1)

TartanLlama
TartanLlama

Reputation: 65600

Your problem isn't in your conversion operator declaration, it's your Baz constructor:

Baz(Foo<int>&);

This takes a non-const reference to Foo<int>. When you implicitly convert a Bar<S> to a Foo<T>, you are generating a temporary object, which can't bind to a non-const reference. If you change it to take the argument by reference-to-const, it works.

Baz(const Foo<int>&);

You can happily declare specific operators for specific types if you want:

template <typename S> struct Bar
{
    template <typename T> operator Foo<T>();
    operator Foo<bool>();
};

Bar<float> a{};
Foo<int> b = a; //calls operator Foo<T>
Foo<bool> c = a; //calls operator Foo<bool>

Upvotes: 1

Related Questions