2013-02-22 4 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); 
} 

Есть ли способ, чтобы этот код автоматически конвертировать из point в vec<2>?

Я знаю, что может привести к перегрузке foo и bar для обеспечения 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

@James: Ошибки сообщаются для строк с вызовами функций, хотя в примечаниях упоминаются и другие другие строки. Не стесняйтесь копировать и компилировать код выше, поскольку он является автономным. В разделе 3.9.1 из [стандарта C++ 11] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf) имена 'unsigned', поэтому почему это не тип C++, который используется совместно с C? – MvG

+0

Нет, он объявляет 'unsigned char',' unsigned short int', 'unsigned int',' unsigned long int' и 'unsigned long long int' как неподписанные целые типы. – 2013-02-22 07:08:26

ответ

8

Там нет прямого способа получить преобразование из point в vec<2>, потому что в то время, когда вызов функции foo(v1,p1) обрабатываются, функция foo, что ожидает 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>.

Полностью рабочий пример здесь: http://liveworkspace.org/code/nZJYu$11

Не совсем просто, но может быть стоит, если у вас действительно есть очень много различных комбинаций аргументов.

+0

@MvG Я изменил «полный рабочий пример». Моя реализация «bar» была неправильной в первой версии. – jogojapan

Смежные вопросы