Search code examples
c++functiontemplatesnamespacesfriend

Friend function template with non type paramer in outer namespace doesn't friend


The function make() has non type template parameter N. That is decrared and defined in the global namespace.

The class target is defined in the namespace ns. That has a private constructor.

I want to create the target instance, so I added friend declaration for the function template make().

However, compiler reports error calling a private constructor of class 'ns::target'.

If I removed the namespace ns, error is disappered. Why does it happen? Is there any good way to declare friend function template in this case?

Here is the code example that reproduces the issue. If you set USE_NS to 0, ns is removed and works fine.

#include <cstddef>
#include <array>
#include <iostream>

#define USE_NS 1 // if set to 0, works fine

// fwd
template <std::size_t N>
std::array<int, N> make(int i);

#if USE_NS
namespace ns {
#endif
    struct target {
    private:
        template <std::size_t N>
        friend
        std::array<int, N> make(int i);

        target(int i) {
            std::cout << i << std::endl;
        }
};
#if USE_NS
} // namespace ns
#endif

// def
template <std::size_t N>
std::array<int, N> make(int i) {

#if USE_NS
    auto p = ns::target(i);
#else
    auto p = target(i);
#endif
    (void)p;

    return std::array<int, N>{};
}

int main() {
    make<2>(20);
    make<4>(40);
}

godbolt link: https://godbolt.org/z/ado8nMfbW

I aslo tried declare the explicit specialization of make() function template.

        friend
        std::array<int, 2> make<2>(int i);
        friend
        std::array<int, 4> make<4>(int i);

It works with namespace. But I can't use this approach because the value and numer of N is unpredictable.

godbolt link: https://godbolt.org/z/Koas15hef


Solution

  • You need two things to do this.

    First use the scope resolution operator :: to tell the compiler that you want to befriend the global make function template.

    Second use parenthesis around ::make like (::make) to tell the compiler that make is not a member function of std::array. If you don't provide the parenthesis then the compiler will produce the error that there is no member function named make in std::array.

    So the solution is:

    template <std::size_t N> friend std::array<int, N> (::make)(int i);
    

    Working demo


    The second reason is also explained in declaring a friend function in the global namespace that returns a template class