Reputation: 13486
Kind of a continuation from my previous question. What I've got is a bunch of functions that form a sfinae depencency chain like so (let "A -> B" notation mean that existence of A depends on existence of B):
S::f_base -> S::f -> ns::f_ -> f -> T::f
where T is the template argument. It's implemented like this:
#include <utility>
struct S;
template <typename T>
auto f(S& s, T const& t) -> decltype(t.f(s), void())
{
t.f(s);
}
namespace ns
{
template <typename T>
auto f_(S& s, T const& t) -> decltype(f(s, t), void())
{
f(s, t);
}
}
struct S
{
template <typename T>
auto f(T const& t) -> decltype(ns::f_(std::declval<S&>(), t), void())
{
ns::f_(*this, t);
}
template <typename T>
auto f_base(T const* t_ptr) -> decltype(f(*t_ptr), void())
{
f(*t_ptr);
}
};
struct pass
{
void f(S&) const
{
}
};
struct fail
{
};
int main()
{
S s;
s.f(pass()); // compiles
//s.f(fail()); // doesn't compile
return 0;
}
and works as intended. The problem arises when I attempt to move definitions of S::f
and S::f_base
outside the class body, like so:
#include <utility>
struct S;
template <typename T>
auto f(S& s, T const& t) -> decltype(t.f(s), void())
{
t.f(s);
}
namespace ns
{
template <typename T>
auto f_(S& s, T const& t) -> decltype(f(s, t), void())
{
f(s, t);
}
}
struct S
{
template <typename T>
auto f(T const& t) -> decltype(ns::f_(std::declval<S&>(), t), void());
template <typename T>
auto f_base(T const* t_ptr) -> decltype(f(*t_ptr), void());
};
template <typename T>
auto S::f(T const& t) -> decltype(ns::f_(std::declval<S&>(), t), void())
{
ns::f_(*this, t);
}
template <typename T>
auto S::f_base(T const* t_ptr) -> decltype(f(*t_ptr), void()) // <---- HERE ---
{
f(*t_ptr);
}
int main()
{
return 0;
}
On the line marked by arrow GCC 4.7.1
expresses it's dissatisfaction:
error: prototype for 'decltype ((((S*)0)->S::f((* t_ptr)), void())) S::f_base(const T*)' does not match any in class 'S'
error: candidate is: template decltype ((((S*)this)->S::f((* t_ptr)), void())) S::f_base(const T*)
I tried to explicitly specify which f
I'm using in f_base
by prepending it (in both declaration and definition) with std::declval<S&>().
, but the error persists.
I know I can modify the dependency graph like so:
S::f_base ->
-> ns::f_ -> f -> T::f
S::f ->
to make S::f_base
depend on ns::f_
along with S::f
, but is there a way to do this with the first dependency graph?
Upvotes: 4
Views: 633
Reputation: 2870
GCC 4.X is not the best for meta programing
I have manage to make it compile in 4.7.3 (live) :
#include <utility>
struct S;
template <typename T>
auto f(S& s, T const& t) -> decltype(t.f(s), void())
{
t.f(s);
}
namespace ns
{
template <typename T>
auto f_(S& s, T const& t) -> decltype(f(s, t), void())
{
f(s, t);
}
}
// some std::void_t like but GCC4.x compatible
template<class T>
struct void_t
{
using type = typename std::enable_if<std::is_same<T,T>::value >::type;
};
struct S
{
template <typename T>
auto f(T const& t) -> decltype(ns::f_(std::declval<S&>(), t), void());
template <typename T>
auto f_base(T const* t_ptr) -> typename void_t< decltype (::f(*std::declval<T const*>()))>::type ;
};
template <typename T>
auto S::f(T const& t) -> decltype(ns::f_(std::declval<S&>(), t), void())
{
ns::f_(*this, t);
}
// ::f is needed, fail if just 'f'
template <typename T>
auto S::f_base(T const* t_ptr) -> typename void_t< decltype (::f(*std::declval<T const*>()))>::type
{
f(*t_ptr);
}
int main()
{
return 0;
}
Note:
In GCC4.X I have seen some weird stuff like :
template<class T>
struct void_t
{
using type = void;
};
Where GCC replace void_t<decltype(expr)>::type
by void
even if expr
is not valid. That why I use this implementation of void_t
.
Upvotes: 1