Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Member detection using void_t

Tags:

c++

c++14

For member detection in C++14 I used code based on the example here, but it does no seem to work.

A complete example:

#include <string>

template <typename...>
using void_t = void;

template <typename, typename = void> class HasMember_substr : public std::false_type {};
template <typename T> class HasMember_substr<T, void_t<typename T::substr>> : public std::true_type {};

template <typename, typename = void> class HasMember_fff : public std::false_type {};
template <typename T> class HasMember_fff<T, void_t<typename T::fff>> : public std::true_type {};

static_assert(HasMember_substr<std::string>::value, "");
static_assert(!HasMember_fff<std::string>::value, "");

int main() { return 0; }

Compiled using clang++ --std=c++14 test.cpp on OS X, compiler version (clang++ --version): Apple LLVM version 7.0.2 (clang-700.1.81)

The second assert succeeds, but the first fails. Why? I have also tried using decltype(T::substr) instead of typename T::subset, with the same result.

like image 446
Jan Avatar asked Dec 21 '15 18:12

Jan


1 Answers

Looking for T::substr is not the same as looking for a member function called substr. gcc.godbolt.org example

You can check if a member function exists by using std::declval<T>() and using decltype to get the return type of the member function.

If the member function exists, decltype(...) will be a well-formed expression and will not trigger SFINAE - therefore the the static_assert will work correctly.

#include <string>
#include <type_traits>
#include <utility>

template <typename...>
using void_t = void;

template <typename, typename = void> 
class HasMember_substr : public std::false_type {};

template <typename T> 
class HasMember_substr<T, void_t<
     decltype(std::declval<T>().substr(1, 1))>
> : public std::true_type {};

static_assert(HasMember_substr<std::string>::value, "");

int main() { return 0; }

Note that decltype(std::declval<T>().substr(1, 1)) checks whether T has a substr member that can be called with arguments 1, 1. (This is not guaranteed to be a member function, it could also be a functor data member, for example.)


As said by AndyG in the comments, another possible approach is using decltype to "validate" the type of a member function pointer.

Example:

HasMember_substr<T, void_t< decltype(&T::substr)>

Note that this will not work if the name substr is overloaded, and it is not guaranteed to work with any type in the standard library.

like image 136
Vittorio Romeo Avatar answered Oct 18 '22 03:10

Vittorio Romeo