2016-04-09 18 views
3

Warum liefert der folgende Code den Compilerfehler keine Übereinstimmung für operator*?Warum wird mein überladener Multiplikationsoperator nicht erkannt?

template<class E> 
class vector_expression {}; 

template<class Tuple> 
class vector 
    : public vector_expression<vector<Tuple>> 
{ 
public: 
    using value_type = typename Tuple::value_type; 
}; 

namespace detail 
{ 
    template<typename T> 
    class scalar 
     : public vector_expression<scalar<T>> 
    {}; 
} 

template<class E1, class E2, class BinaryOperation> 
class vector_binary_operation 
    : public vector_expression<vector_binary_operation<E1, E2, BinaryOperation>> 
{ 
public: 
    template<class F1, class F2> 
    vector_binary_operation(F1&& e1, F2&& e2, BinaryOperation op) 
     : m_e1(std::forward<F1>(e1)), m_e2(std::forward<F2>(e2)), 
      m_op(std::move(op)) 
    { } 

private: 
    E1 m_e1; 
    E2 m_e2; 
    BinaryOperation m_op; 
}; 

template<class E> 
vector_binary_operation<detail::scalar<typename E::value_type>, E, std::multiplies<>> operator*(typename E::value_type value, E&& e) { 
    return { std::move(value), std::forward<E>(e), std::multiplies<>{} }; 
} 
template<class E> 
vector_binary_operation<E, detail::scalar<typename E::value_type>, std::multiplies<>> operator*(E&& e, typename E::value_type value) { 
    return { std::forward<E>(e), std::move(value), std::multiplies<>{} }; 
} 

int main() 
{ 
    vector<std::array<double, 3>> x; 
    3 * x; 

    return 0; 
} 

DEMO

+2

Ihr "DEMO" -Link funktioniert bei mir nicht. –

+2

FYI, '3' ist ein' int', kein 'double'. Vorlagen sind anfällig für solche Dinge. – Cornstalks

+0

@MartinBonner Entschuldigung, behoben. – 0xbadf00d

Antwort

3

schreiben Sie haben zwei operator* überlastet, die (abgesehen von den Rückgabetyp für den Moment) sind:

template <class E> 
R operator*(typename E::value_type, E&&); 

template <class E> 
R operator*(E&&, typename E::value_type); 

In beiden Fälle ist ein Argument ein nicht abgeleiteter Kontext. Fangen wir mit der 2. Überladung an. Wenn wir mit 3 * x anrufen, wird E als int abgeleitet, gibt es keine int::value_type, so dass ein Substitutionsfehler ist.

In der ersten Überladung leiten wir E als vector<std::array<double, 3>>& ab. Beachten Sie, dass es sich um eine Referenz handelt. Daher gibt es keine E::value_type, weil es ein Referenztyp ist. Sie müssen zuerst diesen Teil entfernen (für beide Überladungen). Der einfachste Weg ist es, ein zweites notleidenden Template-Argument einzuführen, die eine un referenzierten Version von E ist:

template<class E, class ER = std::remove_reference_t<E>> 
vector_binary_operation<detail::scalar<typename ER::value_type>, ER, std::multiplies<>> 
operator*(typename ER::value_type value, E&& e); 

Mit diesem Update, jetzt Ihr Code nicht für einen Unterschied Grund nicht kompilieren: scalar keinen Konstruktor mit . Aber das ist ein unabhängiges Problem.

+0

'Zerfall' ist kürzer zu schreiben (und die zusätzlichen Transformationen es ist harmlos) –

+0

Sie haben Recht, ich habe gerade bemerkt, dass ich vergessen hatte schreibe 'std :: decay_t ' statt 'E'. – 0xbadf00d

+0

@TC Es ist kürzer, aber ich bevorzuge manchmal' remove_reference' für die Klarheit, dass wir eigentlich nur eine Referenz entfernen. – Barry

1

Der Code schlägt fehl, da x eine L-Wert-Referenz auf das Sie die :: Zugang Betreiber nicht anwenden können. Um dies zu tun, sollten Sie die abgeleitete Typ E erste std::decay_t, dh

typename std::decay_t<E>::value_type