Home > Enterprise >  Passing templated friend function of a class to other function as parameter results in error
Passing templated friend function of a class to other function as parameter results in error

Time:06-17

The following code demonstrates the problem.

#include <functional>

namespace test {

template <class T>
class A {
   public:
    friend auto foo(const A& obj) { return 1; }
};

template <class Function, class... Args>
void bar(Function f, Args&&... args) {
    const auto result = std::invoke(f, std::forward<Args>(args)...);
    // do stuff with result
}

}  // namespace test

auto main() -> int {
    test::A<int> value;
    test::bar(foo<int>, value);  // results in compile time error
    test::bar(test::foo<int>, value);  // results in compile time error
    return 0;
}

Here is the here link to compiler explorer

It probably has to do something with argument dependent lookup, but i cannot really get it.

CodePudding user response:

The problem is that the friend declaration for foo that you've provided is for a non-template function but while calling bar you're trying to use foo as a template.

There are 2 ways to solve this.

Method 1

Here we provide a separate parameter clause for foo.

namespace test 
{
   
    template <class T> class A {
    public:
        template<typename U> //separate parameter clause added here
        friend auto foo(const A<U>& obj);
    };
//definition
template<typename U>
auto foo(const A<U>& obj) { return 1; }
    
template <class Function, class... Args>
void bar(Function f, Args&&... args) {
    const auto result = std::invoke(f, std::forward<Args>(args)...);
        // do stuff with result
    }

}  // namespace test

auto main() -> int {
    test::A<int> value;
    test::bar(test::foo<int>, value);  //works
    return 0;
}

Working demo

Method 2

Here we provide a forward declarations for the function template foo and then define it later outside the class.

namespace test 
{
    //forward declarations
    template <class T> class A;
    template<typename TT> auto foo(const A<TT>& obj);

    template <class T> class A {
    public:
        //friend declaration
        friend auto foo<>(const A& obj);
//---------------------^^---------------->note the angle brackets
    };
//definition
template<typename TT>
auto foo(const A<TT>& obj) { return 1; }
    
template <class Function, class... Args>
void bar(Function f, Args&&... args) {
    const auto result = std::invoke(f, std::forward<Args>(args)...);
        // do stuff with result
    }

}  // namespace test

auto main() -> int {
    test::A<int> value;
    test::bar(test::foo<int>, value);  //works
    return 0;
}

Working demo

  • Related