c++ - 成员函数检测的递归type_traits

标签 c++ c++11 templates sfinae enable-if

我正在尝试递归地应用 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())> *);

参见 https://wandbox.org/permlink/MpohZzxvZdurMArP

关于c++ - 成员函数检测的递归type_traits,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47954023/

相关文章:

c++ - 如何获取通用lambda的返回类型和参数类型?

c++ - 编译模板方面对 char、signed char 或 unsigned char 参数类型的函数重载

c++ - c2955 错误 - 使用类模板需要参数列表

c++ - 使用 Waf 查找本地外部库

java - 如何释放字符**

c++ - 添加 : in front of variable gives wrong result

c++ - 在 C++ 中注册派生类

c++ - 使用模板检查不适用于 int 的成员变量

c++ - std::unique_ptr 试图引用已删除的函数

c++ - 函数中不调用复制赋值