Reputation: 10982
Playing with SFINAE for method detection in C++11, I wrote this little running example:
#include <type_traits>
struct Foo
{
Foo();// = delete;
Foo(int);
void my_method();
};
template <typename T, typename ENABLE = void>
struct Detect_My_Method
: std::false_type
{
};
template <typename T>
struct Detect_My_Method<T, decltype(T().my_method())>
: std::true_type
{
};
int main()
{
static_assert(!Detect_My_Method<double>::value, "");
static_assert(Detect_My_Method<Foo>::value, "");
}
that worked as expected.
However if I delete the empty constructor of Foo:
struct Foo
{
Foo() = delete;
Foo(int);
void my_method();
};
the example is not working anymore and I get this error message:
g++ -std=c++11 declVal.cpp
declVal.cpp: In function ‘int main()’:
declVal.cpp:33:3: error: static assertion failed
static_assert(Detect_My_Method<Foo>::value, "");
Question: explanation and how to solve that?
Upvotes: 2
Views: 1102
Reputation: 10982
When the empty constructor is deleted the construction:
decltype(Foo().my_method());
is not valid anymore and the compiler complains immediately
error: use of deleted function ‘Foo::Foo()’
One solution is to use std::decval<T>()
Converts any type T to a reference type, making it possible to use member functions in decltype expressions without the need to go through constructors.
Hence replacing:
template <typename T>
struct Detect_My_Method<T, decltype(T().my_method())>
: std::true_type
{
};
by
template <typename T>
struct Detect_My_Method<T, decltype(std::declval<T>().my_method())>
: std::true_type
{
};
solves the problem.
Learned lesson:
decltype(Foo().my_method()); // invalid
decltype(std::declval<Foo>().my_method()); // fine
are not equivalent.
Upvotes: 8
Reputation: 136286
In addition, there is another way to define the test that requires neither a reference or a pointer to the object nor a specific signature of the function:
template<class T>
typename std::is_member_function_pointer<decltype(&T::my_method)>::type test_member_function_my_method(int);
template<class T>
std::false_type test_member_function_my_method(...);
template<class T>
using has_member_function_my_method = decltype(test_member_function_my_method<T>(0));
Usage:
static_assert(!has_member_function_my_method<double>::value, "");
static_assert(has_member_function_my_method<Foo>::value, "");
Upvotes: 4