【问题标题】:Implicit conversion to template隐式转换为模板
【发布时间】:2013-02-22 06:43:37
【问题描述】:

我下面的示例表明,从非模板类型到模板类型的隐式转换不会像只涉及非模板类型的转换那样无缝。有没有办法让它们仍然工作?

例子:

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);
}

有没有办法让这段代码从point自动转换为vec&lt;2&gt;

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

似乎转换构造函数和强制转换运算符都不足以实现这一点。至少我的 gcc 4.7.1 报告了 no matching function call,尽管它确实在通知中命名了所需的函数,并指出 ‘point’ is not derived from ‘vec&lt;d&gt;’

【问题讨论】:

  • 哪一行给出了这个错误?另外,unsigned 不是 C++ 类型。
  • @James:错误是针对函数调用的行报告的,尽管通知也提到了其他各种行。随意复制和编译上面的代码,因为它是独立的。 the C++11 standard 的第 3.9.1 节命名为 unsigned,那为什么不是 C++ 类型,与 C 共享?
  • 不,它将unsigned charunsigned short intunsigned intunsigned long intunsigned long long int 声明为无符号整数类型。
  • 但我离题了。 :) 理论上,只提供转换运算符就足够了。在 vecpoint 之间存在循环引用,因为它目前存在,这可能会导致编译器感到困惑。
  • 我可以强制它工作的唯一变体是调用foo&lt;2&gt;( v2, p1 )。顺便说一句,无需将operator vec&lt;2&gt;() 添加到point

标签: c++ casting overloading implicit-conversion


【解决方案1】:

没有直接的方法可以将point 转换为vec&lt;2&gt;,因为在处理函数调用foo(v1,p1) 时,函数foo 需要vec&lt;2&gt; 作为第二个参数还不存在。它只是一个函数模板,为了将其实例化为 foo(const vec&lt;2&gt; &amp;,const vec&lt;2&gt; &amp;),必须给出具有这些确切参数类型的函数调用。

为了使代码工作,编译器必须猜测 both 如何实例化模板参数, 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)); }

然后将foobar函数实现为通用模板(接受各种类型,不仅vec&lt;d&gt;,使用上面定义的make_vec将给定类型转换为正确的类型vec&lt;d&gt; ):

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)...); }

对于bar,您还需要一种计算返回类型的方法,即vec&lt;d1+d2+d3...&gt;。为此,需要一个总和计算器,也是模板:

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;
};

那么bar()的返回类型就是vec&lt;dsum&lt;T,Ts...&gt;::value&gt;

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

不是很简单,但如果您确实有非常多不同的参数组合,可能值得。

【讨论】:

  • @MvG 我已经修改了“完整的工作示例”。我的bar 实现在第一个版本中是错误的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多