Edit2 : the previous solution caused some errors down the line so for now I am stuck with my member public.
Edit : I thought I had a solution, until I tried on Clang.
The error is about unableness to find a corresponding template.
However it solves itself if I add, before the first forward-declarations to the path
function :
template<typename T, typename U=void>
Path<T> path(const T& obj);
But then GCC would not build it anymore so I fixed it with some macros...
#if defined(__clang__)
template<typename T, typename U = void>
Path<T> path(const T& obj);
#elif defined(__GNUC__)
template<typename T, typename U>
Path<T> path(const T& obj);
#endif
Here is a way to test the code : http://goo.gl/dafN8K
Original question
I found the following answers but they did not solve my problem :
Overloading friend operator << for template class
-> Not good for me because I want to befriend templates, not specializations with the class template parameters, and don't want to inline my code in this class.
Issue with friend template functions clang++ / msvc++ and enable_if
-> I don't want to change my function to have enable_if
in the return type, it makes the code absolutely unreadable.
So, here is my minimal non working code. It works if m_path_cache
is made public but of course I don't want a cache in my public interface...
#include <type_traits>
class IdentifiedObjectAbstract
{
};
template<typename T>
struct Path;
namespace ANamespace
{
template<typename T, std::enable_if_t<
std::is_base_of<
IdentifiedObjectAbstract,
T
>::value
>* = nullptr
>
Path<T> path(const T& obj);
template<typename T, std::enable_if_t<
!std::is_base_of<
IdentifiedObjectAbstract,
T
>::value
>* = nullptr
>
Path<T> path(const T& obj);
}
template<typename T>
class IdentifiedObject : public IdentifiedObjectAbstract
{
////////
// I need to befriend the first template function here.
////////
private:
mutable Path<T> m_path_cache;
};
template<typename Object>
struct Path
{
};
namespace ANamespace
{
template<typename T, std::enable_if_t<
std::is_base_of<
IdentifiedObjectAbstract,
T
>::value
>* = nullptr
>
Path<T> path(const T& obj)
{
return obj.m_path_cache;
}
template<typename T, std::enable_if_t<
!std::is_base_of<
IdentifiedObjectAbstract,
T
>::value
>* = nullptr
>
Path<T> path(const T& obj)
{
return Path<T>{};
}
}
class A : public IdentifiedObject<A>
{};
void f()
{
A a;
auto path = ANamespace::path(a);
}
I also tried befriending functions such as :
template<typename U>
friend Path<U> path(const U& obj);
template<typename U, typename V>
friend Path<U> path(const U& obj);
template <typename U, std::enable_if_t<std::is_base_of<IdentifiedObjectAbstract, U>::value>*>
friend Path<U> path(const U& obj);
template <typename U, std::enable_if_t<std::is_base_of<IdentifiedObjectAbstract, U>::value>* = nullptr>
friend Path<U> path(const U& obj);
but to no avail.