MSVC bug? Template friend function cannot access private members despite template friend declaration

89 Views Asked by At

I have the following C++ code:

template<typename T>
class Foo;

template<typename T, typename U>
Foo<T> operator+(Foo<T> lhs, const Foo<U>& rhs);

template<typename T>
class Foo {
    template<typename>
    friend class Foo;

    T inner;

public:
    Foo(T i) : inner(i) {}

    template<typename U>
    friend Foo<T> operator+(Foo<T> lhs, const Foo<U>& rhs) {
        lhs.inner += rhs.inner;
        return lhs;
    }
};

int main() {
    Foo<int> a = 4;
    Foo<unsigned> b = 5;
    Foo<int> c = a + b;
}

godbolt

It compiles fine with GCC and clang, but compilation fails with an error in MSVC v19.37 and in Visual Studio 2015:

example.cpp
<source>(19): error C2248: 'Foo<unsigned int>::inner': cannot access private member declared in class 'Foo<unsigned int>'
<source>(12): note: see declaration of 'Foo<unsigned int>::inner'
<source>(26): note: see declaration of 'Foo<unsigned int>'
<source>(27): note: see reference to function template instantiation 'Foo<int> operator +<unsigned int>(Foo<int>,const Foo<unsigned int> &)' being compiled
<source>(27): note: see the first reference to 'operator +' in 'main'
Compiler returned: 2

This seems to be some kind of issue with MSVC. Is there a workaround? Or is there something else I'm missing?

2

There are 2 best solutions below

0
On BEST ANSWER

The best solution I've found is to move the actual implementation into a static method, and call that static method inside the friend function:

template<typename T>
class Foo {
    template<typename>
    friend class Foo;

    T inner;

    template<typename U>
    static Foo op_add(Foo lhs, const Foo<U>& rhs) {
        lhs.inner += rhs.inner;
        return lhs;
    }

public:
    Foo(T i) : inner(i) {}

    template<typename U>
    friend Foo operator+(Foo lhs, const Foo<U>& rhs) {
        return op_add(lhs, rhs);
    }
};

godbolt

7
On

There are two problems with your example code.

  1. you are using Foo template parameter on friend function. This has impact on how compiler sees this declaration and confuses compiler (that is why for while I deleted first version of my answer). Friend function should not depend on T!

  2. Defining function in place of where friendship is declared is buggy on all compilers even when generic programing is not in use. So I would recommend you to avoid this as a rule of thumb.

If you define function template with own template parameters and outside of scope Foo class template it works without nasty hacks.

template<typename T>
class Foo {
    T inner;

public:
    Foo(T i) : inner(i) {}

    T get() const {
        return inner;
    }

    template<typename W, typename U>
    friend auto operator+(Foo<W> lhs, const Foo<U>& rhs) -> Foo<W>;
};

https://godbolt.org/z/K8e3dKf4v

Note also I removed some noise code from your example.