隐式转换为模板

MvG*_*MvG 5 c++ casting overloading implicit-conversion

我在下面的示例表明,从非模板类型到模板类型的隐式转换将无法像仅涉及非模板类型的那些无缝转换.有没有办法使它们工作?

例:

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);
}
Run Code Online (Sandbox Code Playgroud)

有没有办法让这段代码自动转换pointvec<2>

我知道我可以重载foobar允许point参数,vec使用显式转换委托给实现.但是对所有参数组合执行此操作将变得乏味,特别是对于具有许多此类参数的函数.所以我对我必须为每个函数的每个参数组合复制代码的解决方案不感兴趣.

似乎转换构造函数和转换运算符都不足以实现这一点.至少我的gcc 4.7.1报告no matching function call,虽然它确实在通知中命名了所需的功能,说明了这一点‘point’ is not derived from ‘vec<d>’.

jog*_*pan 8

有没有直接的方法从得到的转化pointvec<2>的,因为在当函数调用时foo(v1,p1)被处理,函数foo期望一个vec<2>作为第二个参数尚不存在.它只是一个函数模板,为了将它实例化为a foo(const vec<2> &,const vec<2> &),必须给出具有这些精确参数类型的函数调用.

为了使代码工作,编译器会纷纷猜测双方如何实例化一个模板参数,以及什么类型的point参数转换为.这在一般情况下太多了(尽管在您的特定代码中它看起来很简单,因为没有其他可能的方法来解释程序员的意图).

在解决这个问题上,我唯一能想到的就是创建高度模板化的转换函数:

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)); }
Run Code Online (Sandbox Code Playgroud)

然后实现foobar作为通用模板的函数(接受各种类型,不仅vec<d>使用make_vec上面定义的将给定类型转换为正确的类型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)...); }
Run Code Online (Sandbox Code Playgroud)

bar你的情况下你还需要一种方法来计算返回类型,即vec<d1+d2+d3...>.为此,需要一个总和计算器,也是模板化的:

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;
};
Run Code Online (Sandbox Code Playgroud)

然后,返回类型bar()vec<dsum<T,Ts...>::value>.

这里有一个完整的例子:http://liveworkspace.org/code/nZJYu$11

不完全简单,但如果你真的有很多不同的参数组合,那么它们可能是值得的.