我的下面的示例表明,从非模板类型到模板类型的隐式转换将无法像仅涉及非模板类型的那些无缝转换。有没有办法让它们继续工作?
示例:
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<2>
?
我知道我可以重载foo
和bar
以允许point
个参数,使用显式转换委托给vec
实现。但是对所有参数组合执行此操作将变得乏味,特别是对于具有许多此类参数的函数。所以我对我必须为每个函数的每个参数组合重复代码的解决方案不感兴趣。
似乎转换构造函数和强制转换运算符都不足以实现此目的。至少我的gcc 4.7.1报告no matching function call
,虽然它确实在通知中命名了所需的功能,并说明‘point’ is not derived from ‘vec<d>’
。
答案 0 :(得分:8)
没有直接的方法可以从point
转换为vec<2>
,因为在处理函数调用foo(v1,p1)
时,需要一个函数foo
a vec<2>
作为第二个参数尚不存在。它只是一个函数模板,为了将它实例化为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)); }
转换成。这在一般情况下太多了(尽管在您的特定代码中它看起来很简单,因为没有其他可能的方法来解释程序员的意图)。
就解决这个问题而言,我唯一能想到的就是创建高度模板化的转换函数:
foo
然后将bar
和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)...); }
):
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;
};
。为此,需要一个总和计算器,也是模板化的:
bar()
然后,vec<dsum<T,Ts...>::value>
的返回类型为{{1}}。
这里有一个完整的例子:http://liveworkspace.org/code/nZJYu$11
不完全简单,但如果你真的有很多不同的参数组合,那么它们可能是值得的。