2013-02-22 16 views
5

Mój przykład poniżej sugeruje, że niejawne konwersje z typów innych niż szablony na typy szablonów nie będą działać tak bezproblemowo, jak te obejmujące jedynie typy inne niż szablony. Czy istnieje sposób, aby je mimo wszystko działały?Niejawna konwersja do szablonu

Przykład:

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

Czy istnieje sposób, aby ten kod automatycznej konwersji z point do vec<2>?

wiem, że mogę przeciążać foo i bar aby umożliwić point argumentów, delegując do realizacji vec użyciu wyraźnej konwersji. Ale zrobienie tego dla wszystkich kombinacji parametrów stanie się żmudne, szczególnie dla funkcji z wieloma takimi parametrami. Nie interesują mnie rozwiązania, w których muszę kopiować kod dla każdej kombinacji parametrów każdej funkcji.

Wygląda na to, że ani konstruktor konwersji, ani operator rzutowania nie są w stanie tego osiągnąć. Przynajmniej mój gcc 4.7.1 zgłasza no matching function call, mimo że nazwa żądanej funkcji w zawiadomieniu, stwierdzając, że ‘point’ is not derived from ‘vec<d>’.

+0

Która linia podaje ten błąd? Ponadto "unsigned' nie jest typem C++. –

+0

@James: Błędy są zgłaszane dla linii z wywoływanymi funkcjami, mimo że w notatkach wspomniano również o innych liniach. Możesz skopiować i skompilować powyższy kod, ponieważ jest on samodzielny. Sekcja 3.9.1 [standard C++ 11] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf) nazywa się "unsigned", więc dlaczego to nie jest typ C++, współdzielony z C? – MvG

+0

Nie, deklaruje 'unsigned char',' unsigned short int', 'unsigned int',' unsigned long int' i 'unsigned long long int' jako unsigned integer types. –

Odpowiedz

8

Nie ma bezpośredni sposób uzyskać konwersję z point do vec<2>, ponieważ w momencie, gdy są przetwarzane wywołanie funkcji foo(v1,p1), funkcja foo że spodziewa się vec<2> jako drugi argument jeszcze nie istnieje. Jest to po prostu szablon funkcji, i aby był on utworzony do postaci foo(const vec<2> &,const vec<2> &), należałoby podać wywołanie funkcji z tymi dokładnymi typami argumentów.

Aby kodu do pracy, kompilator musiałby odgadnąć zarówno jak instancję parametrów szablonu, i jaki rodzaj point argumentem do konwersji. Jest to zbyt wiele w ogólnym przypadku (chociaż w twoim szczególnym kodzie wydaje się to proste, ponieważ nie ma innego możliwego sposobu interpretowania intencji programisty).

Pod względem rozwiązywania tego, jedyne co mogę myśleć jest stworzenie wysoce matrycy funkcji konwersji:

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

a następnie wdrożyć funkcje foo i bar jako ogólne szablony (akceptując wszystkie rodzaje typów, nie tylko vec<d> korzystając make_vec zdefiniowanego wyżej podanych typów konwersji do odpowiedniego rodzaju 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)...); } 

W przypadku bar potrzebujesz również sposobu obliczenia typu zwrotu, który jest vec<d1+d2+d3...>. Do tego kalkulator suma jest wymagane również na matrycy:

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

Następnie typ powrót bar() jest vec<dsum<T,Ts...>::value>.

przykładem w pełni pracy jest tutaj: http://liveworkspace.org/code/nZJYu$11

Niezupełnie prosta, ale może być warto jeśli naprawdę mają bardzo wiele różnych kombinacji argumentów.

+0

@MvG Zmodyfikowałem "pełny przykład pracy". Moja implementacja 'bar' była błędna w pierwszej wersji. – jogojapan

Powiązane problemy