使用void_t检测成员

Jan*_*Jan 7 c++ c++14

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

一个完整的例子:

#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; }
Run Code Online (Sandbox Code Playgroud)

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

第二个断言成功,但第一个失败.为什么?我也试过用decltype(T::substr)而不是typename T::subset,用相同的结果.

Vit*_*meo 11

寻找T::substr与查找名为的成员函数不同substr.gcc.godbolt.org的例子

您可以通过使用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; }
Run Code Online (Sandbox Code Playgroud)

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


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

例:

HasMember_substr<T, void_t< decltype(&T::substr)>
Run Code Online (Sandbox Code Playgroud)

请注意,如果名称substr过载,这将不起作用,并且不能保证与标准库中的任何类型一起使用.

  • @AndyG如果名称`substr`被重载,这将不起作用 (5认同)
  • "如果名称substr被重载,这将无法工作"......因此[不保证可以使用标准库中的任何类型](http://eel.is/c++draft/member.functions#2 ). (3认同)
  • 确切地说,具有`decltype(std :: declval <T>().substr(0,0))`的版本不检查`T`是否具有`substr`成员函数.它检查`T`是否有一个可以用参数`0,0`调用的`substr`成员.那是不同的.例如,它允许`substr`成为一个仿函数数据成员.另一个例子:它不允许`substr`成为不带参数的成员函数.这很好,可能更接近OP的要求,但在你的答案中更清楚地发布它正在做的事情可能会有所帮助.另外,我建议`1,1`来排除解释为空指针常量. (2认同)
  • 我不认为hvd的要点需要用**注意**.这是一个有趣的事实,值得指出,但没有什么值得谨慎的.关键是测试`decltype(std :: declval <T>().substr(1,1))`适用于你关心的所有情况,你不需要关心它是否是一个函数(和你不需要谨慎). (2认同)