Reputation: 16344
While continuing work from my previous question, I came across different behavior of clang and GCC.
I need to check the member function pointer because I need to know if the function is inherited or not.
When comparing member function pointers in an SFINAE context, member function Foo::foo()
exists, but its body contains code (x.hello()
) which eventually does not compile.
The following code compiles with clang.
GCC however seems to evaluate the function body of Foo::foo()
and exits with an error ('struct Caller' has no member named 'hello'
), despite being in an unevaluated SFINAE context (or so I hope).
#include <iostream>
#include <type_traits>
struct Foo
{
template <typename T> void foo(T&& x) { x.hello(); }
};
struct Caller
{
template <typename T>
auto call(T&& x) -> decltype(
std::enable_if_t<
std::is_same<
decltype(&T::template foo<decltype(*this)>),
void (T::*)(decltype(*this))
>::value
>())
{
//x.foo(*this);
}
};
int main()
{
Caller c;
c.call(Foo());
}
I tested with:
Compiler options for both: -std=c++14 -O2 -Wall -pedantic -pthread
My questions:
Upvotes: 3
Views: 181
Reputation: 30604
In answer to the second question
How can I get the code to compile with GCC?
I would simplify the expression being passed to the decltype()
. If the compilation of the call
method is dependent on the compilation of x.foo(*this);
, then that is what you should use.
struct Foo
{
template <typename T> void foo(T&& x) { x.hello(); }
};
struct Caller
{
template <typename T>
auto call(T&& x, int) -> decltype(x.foo(*this))
{
//x.foo(*this);
}
template <typename T>
void call(T&&, char){ std::cout << "hello" << std::endl;}
};
int main()
{
Caller c;
c.call(Foo(), 0);
}
I think the OP's issue with gcc lies in the address being taken of the function (or decaying to a function pointer if not explicitly taken). I think this is something of a corner case in the standard. The x.hello()
would need to exist (compile) if the method Foo::foo
is required; in general taking the address of something satisfies that, but in an unevaluated context (decltype()
), I'm not sure if that would apply - certainly clang does not require it to exist (nor does MSVC).
In that respect,
Who is right? Clang or GCC?
I suspect clang implements a more permissive reading of the standard and probably more correct reading. The operand to a decltype()
is an unevaluated operand, see [dcl.type.simple]/4;
The operand of the decltype specifier is an unevaluated operand (Clause [expr]).
Upvotes: 2