Search code examples
c++c++11friendautodecltype

Decltype and friend functions in Visual Studio vs G++


I was writing some C++ code to do vector math. It is essential just a thin wrapper around a std::array instance. I wanted to overload the non-member begin() function to return an iterator to the beginning of the backing array. To do this, I wrote a simple friend function with an auto return type and a trailing return type using decltype that just forwarded the call along to the member variable.

It wouldn't compile, and I couldn't figure out why. I started fiddling around with a smaller example and discovered the following code compiles under G++ 4.7, but not under the latest Visual Studio 2012 Professional.

#include <iostream>
#include <array>

template <typename T, size_t size>
class MyClass {

private:
    std::array<T, size> elts;

public:
    friend auto begin(MyClass &a) -> decltype (std::begin(a.elts)) {
        return std::begin(a.elts);
    }

};

int main(void) {
    MyClass<int, 8> instance;
    auto it = begin(instance);
    std::cout << *it << std::endl;
    return 0;
}

The odd thing was this code only compiled in G++ if the private declaration of elts came before the declaration of the begin() function.

In any case, which compiler is right here? Visual Studio or G++?

Edit: The compile error that VS2012 gave was error C2228: left of '.elts' must have class/struct/union


Solution

  • The definition of class template MyClass is not complete by the time you use the expression std::begin(a.elts), so I guess VC has a reason to complain. You cannot use operator . on an incomplete type.

    In any case, you could work around that by using the following:

    #include <iostream>
    #include <array>
    
    template <typename T, size_t size>
    class MyClass 
    {
        // ...
    
        friend typename std::array<T, size>::iterator begin(MyClass &a)
        //     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        {
            return std::begin(a.elts);
        }
    };