Vikram Saran
Vikram Saran

Reputation: 1143

Undefined operator error when using specialised template

I am using the latest version of MSVC with Visual Studio 2015 Community Edition, and I am having some issues with template specialisation and operator overloading.

I have the following (header only) vector class snippet. I have excluded unnecessary code (i.e. identical specialisations for vec2 and vec4).

template <typename T, int n>
struct VectorN
{
    T data[n];

    T& operator[](const int i);
};

template <typename T, int n>
T& VectorN<T, n>::operator[](const int i)
{
    static_assert(i >= 0 && i < n, "Index out of range");
    return data[i];
}

template <typename T, int n>
std::ostream& operator<<(std::ostream& os, const VectorN<T, n>& vec)
{
    for (auto i = 0; i < n; ++i)
    {
        os << vec[i];
    }
    return os;
}

With the following specialisation:

template <typename T>
struct VectorN<T, 3>
{
    union
    {
        T data[3];

        struct
        {
            T x, y, z;
        };
    };
};

typedef VectorN<int, 3> Vec3i;
typedef VectorN<float, 3> Vec3f;

The main function that I am compiling is:

int main(int argc, char *argv[])
{
    Vec3f vec{ 0, 1, 2 };

    std::cout << vec << std::endl;

    char dump;
    std::cin >> dump;
    std::cin.clear();

    return 0;
}   

I would expect that it works, however, I get an Error: C2676 binary '[': 'const VectorN<float,3>' does not define this operator or a conversion to a type acceptable to the predefined operator

I believe that the ostream operator is working as intended, but that the indexing operator is not. Is there anything that I am doing that is obviously wrong, or does the current MSVC simply not support what I am trying to do?

Upvotes: 0

Views: 74

Answers (2)

Vittorio Romeo
Vittorio Romeo

Reputation: 93274

If you want to keep the union-based solution to access a 3-dimensional vector with both data and x, y, z, you can use CTRP to create a base class which is aware of the derived classes' data member:

namespace impl
{
    template <typename T, int n, typename TDerived>
    struct VectorBase
    {
        const T& operator[](const int i) const
        {   
            assert(i >= 0 && i < n);
            return static_cast<const TDerived&>(*this).data[i];
        }
    };
}

Your derived classes can then be implemented as follows:

template <typename T, int n>
struct VectorN : impl::VectorBase<T, n, VectorN<T, n>>
{
    T data[n];
};

template <typename T>
struct VectorN<T, 3> : impl::VectorBase<T, 3, VectorN<T, 3>>
{
    union
    {
        T data[3];

        struct
        {
            T x, y, z;
        };
    };

    VectorN(T ax, T ay, T az) : data{ax, ay, az} {}
};

A working example is available on wandbox.

Upvotes: 1

Edgar Rokjān
Edgar Rokjān

Reputation: 17483

The operator<< works because you have an appropriate overloading for your VectorN templated type.

From the other side, operator[] does not work, because there is no operator[] in the specialized version of VectorN (and compiler kindly tell you about it).

You can either add operator[] to the specialized version of VectorN class or, as @DeiDei mentioned, try to move some functionality to the base class.

Upvotes: 1

Related Questions