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;
}
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;
}