Perché SFINAE non si applica a questo?
Domanda
Sto scrivendo un semplice codice punto mentre provo Visual Studio 10 (Beta 2) e ho colpito questo codice dove mi sarei aspettato che SFINAE iniziasse, ma sembra non farlo:
template<typename T>
struct point {
T x, y;
point(T x, T y) : x(x), y(y) {}
};
template<typename T, typename U>
struct op_div {
typedef decltype(T() / U()) type;
};
template<typename T, typename U>
point<typename op_div<T, U>::type>
operator/(point<T> const& l, point<U> const& r) {
return point<typename op_div<T, U>::type>(l.x / r.x, l.y / r.y);
}
template<typename T, typename U>
point<typename op_div<T, U>::type>
operator/(point<T> const& l, U const& r) {
return point<typename op_div<T, U>::type>(l.x / r, l.y / r);
}
int main() {
point<int>(0, 1) / point<float>(2, 3);
}
Questo dà error C2512: 'point<T>::point' : no appropriate default constructor available
Dato che si tratta di una beta, ho fatto un rapido controllo di integrità con il compilatore comeau online e concorda con un errore identico, quindi sembra che questo comportamento sia corretto, ma non riesco a capire perché.
In questo caso alcune soluzioni alternative consistono semplicemente nell'integrare decltype(T() / U())
, per assegnare alla classe punto un costruttore predefinito o per utilizzare il decltype sull'espressione del risultato completo, ma ho riscontrato questo errore mentre cercavo di semplificare un errore che stavo ottenendo con una versione di op_div che non richiedeva un costruttore predefinito *, quindi preferirei correggere la mia comprensione del C ++ piuttosto che fare semplicemente ciò che funziona.
Grazie!
*: l'originale:
template<typename T, typename U>
struct op_div {
static T t(); static U u();
typedef decltype(t() / u()) type;
};
Che dà error C2784: 'point<op_div<T,U>::type> operator /(const point<T> &,const U &)' : could not deduce template argument for 'const point<T> &' from 'int'
, e anche per il point<T> / point<U>
sovraccarico.
Soluzione
Non sicuro al 100%. Sembra che il compilatore debba istanziare entrambi i sovraccarichi per determinare quale sia meglio, ma mentre si tenta di istanziare l'altro op_div con T = int
e U = point<float>
, questo porta a un errore che non è coperto da SFINAE (l'errore non è quello op_div non ha tipo in questo caso, ma quel tipo non può essere determinato).
Potresti provare a disabilitare il secondo sovraccarico se il secondo tipo è un punto (boost::disable_if
).
Inoltre, ciò che sembra funzionare è la dichiarazione del tipo di ritorno rinviata (eliminando la struttura op_div, ma a seconda delle funzionalità C ++ 0x supportate dal compilatore):
template<typename T, typename U>
auto
operator/(point<T> const& l, point<U> const& r) -> point<decltype(l.x / r.x)> {
return {l.x / r.x, l.y / r.y};
}
template<typename T, typename U>
auto
operator/(point<T> const& l, U const& r) -> point<decltype(l.x / r)> {
return {l.x / r, l.y / r};
}
Altri suggerimenti
Direi che il tuo errore è qui:
template<typename T>
struct point {
T x, y;
point(T x, T y) : x(x), y(y) {}
};
Cambia la definizione della struttura in questa:
template<typename T>
struct point<T> {
T x, y;
point(T x, T y) : x(x), y(y) {}
};
Se si desidera utilizzare un tipo T generico, è necessario specificarlo nella definizione.