Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit conversion to template

My example below suggests that implicit conversions from non-template types to template types won't work as seamlessly as those only involving non-template types. Is there a way to make them work nonetheless?

Example:

struct point;

template<unsigned d> struct vec {
  vec() { }
  // ...
};

template<> struct vec<2> {
  vec() { }
  vec(const point& p) { /* ... */ } // Conversion constructor
  // ...
};

struct point {
  operator vec<2>() { return vec<2>(/* ... */); } // Conversion operator
};

template<unsigned d> vec<d> foo(vec<d> a, vec<d> b) {
  return vec<d>(/* ... */);
}

template<unsigned d1, unsigned d2>
vec<d1 + d2> bar(vec<d1> a, vec<d2> b) {
  return vec<d1 + d2>(/* ... */);
}

int main(int argc, char** argv) {
  point p1, p2;
  vec<2> v2;
  vec<3> v3;
  foo(v2, p1);
  foo(p2, v2);
  foo(p1, p2);
  bar(v3, p1);
}

Is there a way to let this code auto-convert from point to vec<2>?

I know I can overload foo and bar to allow for point arguments, delegating to the vec implementation using an explicit conversion. But doing this for all parameter combinations will become tedious, particularly for functions with many such parameters. So I'm not interested in solutions where I have to duplicate code for every parameter combination of every function.

It appears that neither the conversion constructor nor the cast operator are sufficient to achieve this. At least my gcc 4.7.1 reports no matching function call, although it does name the desired function in a notice, stating that ‘point’ is not derived from ‘vec<d>’.

like image 872
MvG Avatar asked Feb 22 '13 06:02

MvG


People also ask

What is the example of implicit conversion?

Implicit conversions: No special syntax is required because the conversion always succeeds and no data will be lost. Examples include conversions from smaller to larger integral types, and conversions from derived classes to base classes.

What does implicit conversion mean?

An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.

What is implicit type conversion in programming?

Implicit type conversion in C language is the conversion of one data type into another datatype by the compiler during the execution of the program. It is also called automatic type conversion.

What is implicit type casting explain with example?

In Implicit type conversion, Python automatically converts one data type to another data type. This process doesn't need any user involvement. Let's see an example where Python promotes the conversion of the lower data type (integer) to the higher data type (float) to avoid data loss.


1 Answers

There is no direct way to get the conversion from point to vec<2>, because at the time when the function call foo(v1,p1) is processed, a function foo that expects a vec<2> as second argument does not exist yet. It's just a function template, and in order for this to be instantiated to a foo(const vec<2> &,const vec<2> &), a function call with these exact argument types would have to be given.

In order for the code to work, the compiler would have to guess both how to instantiate the template parameters, and what type the point argument to convert to. This is too much in the general case (although in your particular code it appears simple, because there is no other possible way to interpret the intent of the programmer).

In terms of solving this, the only thing I can think of is to create highly templated conversion functions:

template <typename T>
struct make_vec
{ };

template <unsigned d>
struct make_vec<vec<d>>
{
  static constexpr unsigned dim = d;
  using type = vec<dim>;

  static const type &from(const type &v)
  { return v; }
};

template <>
struct make_vec<point>
{
  static constexpr unsigned dim = 2;
  using type = vec<dim>;

  static type from(const point &p)
  { return type(p); }
};

template <typename T>
typename make_vec<typename std::decay<T>::type>::type make_vec_from(T&& arg)
{ return make_vec<typename std::decay<T>::type>::from(std::forward<T>(arg)); }

And then implement the foo and bar functions as general templates (accepting all kinds of types, not only vec<d>, using make_vec defined above to convert the given types to the right kind of vec<d>):

namespace detail {
  /* Your original implementation of foo. */
  template<unsigned d> vec<d> foo(vec<d>, vec<d>) {
    return vec<d>(/* ... */);
  }
}

/* Templated version of foo that calls the conversion functions (which do
   nothing if the argument is already a vec<d>), and then calls the
   foo() function defined above. */
template <typename T, typename... Ts>
typename make_vec<typename std::decay<T>::type>::type foo(T&& arg, Ts&&... args)
{ return detail::foo(make_vec_from(arg),make_vec_from(args)...); }

In the case of bar you also need a way to calculate the return type, which is vec<d1+d2+d3...>. For this, a sum calculator is required, also templated:

template <typename... Ts>
struct dsum {
  static constexpr unsigned value = 0;
};

template <typename T, typename... Ts>
struct dsum<T,Ts...> {
  static constexpr unsigned value = make_vec<typename std::decay<T>::type>::dim + dsum<Ts...>::value;
};

Then, the return type of bar() is vec<dsum<T,Ts...>::value>.

A fully working example is here: http://liveworkspace.org/code/nZJYu$11

Not exactly simple, but might be worth it if you really have extremely many different combinations of arguments.

like image 149
jogojapan Avatar answered Sep 22 '22 19:09

jogojapan