我正在尝试递归地应用 type_trait has_fun
以便 C
仅在 T
时启用其 fun
成员函数> 有一个。
有没有办法让 C::fun
被有条件地检测到?
template <typename T>
struct has_fun {
template <class, class> class checker;
template <typename U>
static std::true_type test(checker<U, decltype(&U::fun)> *);
template <typename U>
static std::false_type test(...);
static const bool value = std::is_same<std::true_type, decltype(test<T>(nullptr))>::value;
};
struct A {
void fun(){
std::cout << "this is fun!" << std::endl;
}
};
struct B {
void not_fun(){
std::cout << "this is NOT fun!" << std::endl;
}
};
template<typename T>
struct C {
void fun() {
static_assert(has_fun<T>::value, "Not fun!");
t.fun();
}
T t;
};
int main(int argc, char const *argv[])
{
std::cout << has_fun<A>::value << std::endl;
std::cout << has_fun<B>::value << std::endl;
std::cout << has_fun<C<A>>::value << std::endl;
std::cout << has_fun<C<B>>::value << std::endl;
}
输出:
1
0
1
1
预期输出:
1
0
1
0
最佳答案
您需要允许编译器将 SFINAE 放在方法上。
模板中发生的所有检查仅考虑函数的签名,因此不会考虑您使用的 static_assert。
解决方案是在签名中添加一个检查。
凭直觉你会写
template<typename T>
struct C {
std::enable_if_t<has_fun<T>::value> fun() {
t.fun();
}
T t;
};
但这不会产生您期望的结果:编译器将拒绝编译 C,即使您不调用 C.fun();
为什么?
如果编译器可以证明它永远不会工作,则允许编译器评估代码并发出错误。 因为当你声明 C 时,编译器可以证明 foo() 永远不会被允许,它会编译失败。
要解决这个问题,您可以强制该方法具有依赖类型,这样编译器就无法证明它总是会失败。
这里是技巧
template<typename T>
struct C {
template<typename Q=T, typename = if_has_fun<Q>>
void fun() {
t.fun();
}
T t;
};
编译器无法证明 Q 永远是 T,我们检查的是 Q,而不是 T,因此只有在调用 fun 时才会执行检查。
完整的工作解决方案 https://wandbox.org/permlink/X32bwCqQDb288gVl
注意:我使用的是实验中的检测器,但您可以使用您的检测器。
不过,您需要替换真正的测试,以检查是否可以正确调用该函数。
template <typename U>
static std::true_type test(checker<U, decltype(std::declval<U>().fun())> *);
关于c++ - 成员函数检测的递归type_traits,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47954023/