c++templatesmultiple-inheritancemixinsclang++

clang 18 and operator overloads from multiple bases


I have upgraded clang from ver 14 to the latest one (18 on trunk) and it seems the only problem I have in my code is operator inheritance from multiple base classes (an example and reference to godbolt below). This is a simplified example where I have a final vector class that derives a few mixins that add functionality. In this case, these mixins are vector-scalar multiplication and vector-vector multiplication, implemented as vector dot product. Both mixings add operator* but both of these operators are specialized to work with specific kinds of types so there should not be any ambiguity. And that worked well until now. So my question is - is this new error is implementation of some standard requirement or this is just a bug in the compiler beta version and my code is okay?

https://godbolt.org/z/ca8Yefjdc

#include <cstddef>
#include <type_traits>
#include <array>

template<
    typename T, size_t N,
    template <typename, size_t> typename FinalTemplate
>
struct VectorData
{
    std::array<T, N> data;
};

template<
    typename T, size_t N,
    template <typename, size_t> typename FinalTemplate
>
struct VectorScalarMultiplication
{
    using Final = FinalTemplate<T, N>;

    template<typename U, typename Enable = std::enable_if_t<(
        std::is_integral_v<U> || std::is_floating_point_v<U>
    )>>
    Final operator*(const U value) const
    {
        auto copy = static_cast<const Final&>(*this);
        for (size_t i = 0; i != N; ++i)
        {
            copy.data[i] *= value;
        }

        return copy;
    }
};



template<
    typename T, size_t N,
    template <typename, size_t> typename FinalTemplate
>
struct VectorVectorMultiplication
{
    using Final = FinalTemplate<T, N>;

    T operator*(const Final& b) const
    {
        auto& a = static_cast<const Final&>(*this);
        T r{};
        for (size_t i = 0; i != N; ++i)
        {
            r += a.data[i] * b.data[i];
        }

        return r;
    }
};

template<typename T, size_t N>
struct Vector
    : public VectorData<T, N, Vector>
    , public VectorScalarMultiplication<T, N, Vector>
    , public VectorVectorMultiplication<T, N, Vector>
{
};

int main()
{
    Vector<float, 3> a;
    return static_cast<int>(a * a);
}

If that helps, adding these lines into the final class solves the issue but kind of breaks the whole idea of mixins:

    using VectorScalarMultiplication<T, N, Vector>::operator*;
    using VectorVectorMultiplication<T, N, Vector>::operator*;

Solution

  • This is a long standing bug in Clang where ambiguous members in base classes were not diagnosed. The example in the bug report linked above uses operator(), but the bug occurred for other operator overloads as well (although regular named functions were diagnosed correctly). The bug was recently fixed.

    So your code was never supposed to work the way it was written, and now it doesn't work since the Clang bug has been fixed.