简体   繁体   中英

std::declval<T>(), SFINAE and deleted constructor

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?

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.

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, "");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM