使用void_t检测成员

时间:2015-12-21 18:29:33

标签: c++ c++14

对于C ++ 14中的成员检测,我使用了基于示例here的代码,但它似乎没有用。

一个完整的例子:

#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; }

在OS X上使用clang++ --std=c++14 test.cpp进行编译,编译器版本(clang++ --version):Apple LLVM version 7.0.2 (clang-700.1.81)

第二个断言成功,但第一个断言失败。为什么?我也尝试使用decltype(T::substr)代替typename T::subset,结果相同。

1 个答案:

答案 0 :(得分:11)

查找T::substr与查找名为substr的成员函数不同。 gcc.godbolt.org example

您可以使用std::declval<T>()并使用decltype来检查成员函数是否存在,以获取成员函数的返回类型。

如果成员函数存在,decltype(...)将是格式正确的表达式,不会触发SFINAE - 因此static_assert将正常工作。

#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; }

请注意,decltype(std::declval<T>().substr(1, 1))会检查T是否有可以使用参数substr调用的1, 1成员。 (此不保证是成员函数,例如,它也可以是仿函数数据成员。)

正如AndyG在评论中所说,另一种可能的方法是使用decltype来验证&#34;成员函数指针的类型。

示例:

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

请注意,如果名称substr已超载且is not guaranteed to work with any type in the standard library已成功,则此功能无效。