2013-02-22 90 views
5

我的示例如下表明,从非模板类型到模板类型的隐式转换将无法像仅涉及非模板类型的那样无缝地转换。有没有办法让他们继续工作?隐式转换为模板

例子:

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

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

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

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

+0

哪条线给出了该错误?另外,'unsigned'不是C++类型。 – 2013-02-22 06:59:20

+0

@詹姆斯:错误报告的功能调用的行,但通知还提及其他各种行。随意复制并编译上面的代码,因为它是独立的。 [C++ 11标准]第3.9.1节(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf)命名'unsigned',那么为什么会那不是C++类型,与C共享? – MvG 2013-02-22 07:04:46

+0

不,它将'unsigned char','unsigned short int','unsigned int','unsigned long int'和'unsigned long long int'声明为无符号整数类型。 – 2013-02-22 07:08:26

回答

8

没有直接的方式来获得pointvec<2>转换,因为当函数调用foo(v1,p1)被处理的时候,期望一个vec<2>为第二个参数的函数foo还不存在。它只是一个函数模板,为了将其实例化为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)); } 

,然后实现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)...); } 

在的情况下您还需要一种计算返回类型的方法,即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>

一个完全工作的例子是在这里:http://liveworkspace.org/code/nZJYu$11

不完全是简单的,但可能是值得的,如果你真的有争论非常多的不同的组合。

+0

@MvG我修改了“完整的工作示例”。在第一个版本中,我的'bar'实现是错误的。 – jogojapan 2013-02-22 07:30:18