5
4

1 回答 1

8

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.

于 2013-02-22T07:11:01.587 回答