Vorlage Freund Funktion und Rückgabetyp Abzug

Hinweis: Diese Frage ist wirklich nah anRückgabetyp-Abzug für klasseninterne Friend-Funktionen, aber ich habe dort keine Antwort auf mein Problem gefunden.

Getestet mit clang 3.4 mit std = c ++ 1y und clang 3.5 mit std = c ++ 14 und std = c ++ 1z

Dieser Code kompiliert:

#include <iostream>

template<class T>
class MyClass {
    public:
        MyClass(T const& a) : impl(a) {}

        template<class T0, class T1> friend auto
        // requires operator+(T0,T1) exists
        operator+(MyClass<T0> const& a, MyClass<T1> const& b)
        {
            return MyClass<decltype(a.impl+b.impl)>{a.impl + b.impl};
        }

        T getImpl() const { return impl; }

    private:
        T impl;
};

int main() {
    MyClass<int> x(2);
    MyClass<long> y(2);

    auto z = x+y;
    std::cout << z.getImpl() << "\n";
}

Nun, wenn ich operator + außerhalb der Klasse definiere, wird es nicht mehr kompiliert:

template<class T>
class MyClass {
    public:
        MyClass(T const& a) : impl(a) {}

        template<class T0, class T1> friend auto
        operator+(MyClass<T0> const& a, MyClass<T1> const& b);

        T getImpl() const { return impl; }
    private:
        T impl;
};

template<class T0, class T1> auto
operator+(MyClass<T0> const& a, MyClass<T1> const& b)
{
    return MyClass<decltype(a.impl+b.impl)>{a.impl + b.impl};
}

Clang 3.4 sagt:

error: use of overloaded operator '+' is ambiguous (with operand types MyClass<int> and MyClass<long>)

Und zeigt dann auf, was es für zwei verschiedene Funktionen hält: die Deklaration in der Klasse und die Definition außerhalb der Klasse.

Meine Frage ist: Ist es ein Clang-Bug oder nur, dass Template-Parameter für eine Friend-Funktion abgeleitet werden, was dazu führt, dass die beiden Funktionen in einigen Fällen nicht gleichwertig sind? Und welche Alternative würden Sie vorschlagen: Operator + zu einer Member-Funktion machen oder Friend-Operator + innerhalb der Klasse definieren (was meiner Meinung nach die Klassenschnittstelle überladen würde)?

Nur zu Ihrer Information, ich habe einen realen Anwendungsfall für solchen Code, bei dem ich versuche, eine Matrixklasse eines Drittanbieters zu verpacken, und ich muss den Rückgabetyp abziehen, da die Ausdrucksvorlage für die verzögerte Auswertung verwendet wird.

Bearbeite: Folgendes funktioniert (aber die Benutzeroberfläche ist immer noch unübersichtlich ...)

template<typename T>
class MyClass
{
    T impl;

public:
    explicit MyClass(T a) : impl(std::move(a)) { }

    T const& getImpl() const { return impl; }

    template<typename T0, typename T1>
    friend auto operator +(MyClass<T0> const& a, MyClass<T1> const& b) -> MyClass<decltype(a.impl + b.impl)>;
};

template<typename T0, typename T1>
auto operator +(MyClass<T0> const& a, MyClass<T1> const& b) -> MyClass<decltype(a.impl + b.impl)>
{
    return MyClass<decltype(a.impl + b.impl)>(a.impl + b.impl);
}

Antworten auf die Frage(2)

Ihre Antwort auf die Frage