pank Asked:2020-10-18 18:35:09 +0000 UTC2020-10-18 18:35:09 +0000 UTC 2020-10-18 18:35:09 +0000 UTC 如何检查类 T 是否有 foo 方法? 772 是否可以C++写类似这样的东西: template<class T> void f(T a) { if ( Существет метод a.foo ) { a.foo(); } else { myfoo(a); } } 那些。该函数的行为应根据类 T 是否具有方法而有所不同foo。 c++ 2 个回答 Voted Best Answer yrHeTateJlb 2020-10-18T20:02:53Z2020-10-18T20:02:53Z 这是这样做的: #include <iostream> struct A{ void foo(){} }; struct B{}; template<class T> struct Test{ typedef void(T::*P)(void); template<class U, P = &U::foo> struct True{char dummy[2];}; typedef char False; static False detect(...); template<class U> static True<U> detect(U*); static const bool exists = (sizeof(False) != sizeof(detect(static_cast<T*>(0)))); }; int main(){ std::cout << std::boolalpha << Test<B>::exists << std::endl; //false std::cout << std::boolalpha << Test<A>::exists << std::endl; //true } 现在谈谈这里发生的事情。该代码使用了一个名为 SFINAE 的成语。缩写 SFINAE 代表替换失败不是错误,意思如下:定义函数重载时,错误的模板实例化不会导致编译错误,但会从最合适的重载候选列表中丢弃。 现在让我们看看这段代码的行为。该类Test定义了两种类型True和False。他们的重要属性是sizeof(False) != sizeof(True<T>). 我们还有两种方法可供使用detect。第一个接受指向 的指针T,第二个接受任意数量的参数(省略号)。省略号是杂食性的,同时在选择重载时优先级最低。 调用时,detect(static_cast<T*>(0)编译器会尝试找到合适的重载。模板版本具有更高的优先级detect。但是如果类型T没有方法void foo(),那么模板的实例化就会失败。但是我们记得替换失败不是错误。编译器将优先使用省略号(点)。 detect我们可以通过返回值的类型找出选择了哪个版本。我们可以通过大小来区分类型。 请注意,代码中没有方法的实现detect。sizeof它利用了它不评估表达式的值这一事实,它立即提取类型。因此这些方法detect将永远不会被调用。 exists因此,如果true类型T有方法,它将在变量中void foo()。 所有代码都在编译时执行,不提供任何开销。 另外,这段代码是由在线编译器以C++98兼容模式编译的 UPD:我在评论中暗示这不能解决你的问题。在某种程度上,这是事实。结果值不能简单地用在if-e 中,因为这会导致编译错误。我们需要一个if可以在编译阶段工作的类似物。模板专业化可以像这样if-a: template<class T, bool b = Test<T>::exists> struct Foo; template<class T> struct Foo<T, true>{ static void foo(const T &t){ t.foo(); } }; template<class T> struct Foo<T, false>{ static void foo(const T &t){ std::cout << "foo" << std::endl; } }; 该结构Foo有两个专业。第一个是为了Test<T>::exists == true,第二个是为了false。好吧,为了方便起见,蛋糕上的小樱桃,除了自动推断参数类型外,没有添加任何新内容: template<class T> void foo(const T &t){ Foo<T>::foo(t); } 现在main它看起来像这样: int main(){ A a; B b; foo(a); //A::foo foo(b); //foo } 完整示例 ixSci 2020-10-18T21:29:20Z2020-10-18T21:29:20Z 以下所有内容都适用于 C++,至少版本 14。你基本上有两个问题:第一个是确定类是否有函数,第二个是根据是否有函数来调用函数。第一个问题很简单地解决了: template<typename T, typename = void> struct HasFoo: std::false_type {}; template<typename T> struct HasFoo<T, std::enable_if_t<std::is_same<decltype(std::declval<T>().foo()), void>::value>>: std::true_type {}; template<typename T> constexpr bool HasFoo_v = HasFoo<T>::value; 它是这样使用的: constexpr bool hasFoo = HasFoo_v<ClassToTest>; 假设我们有以下输入: struct A { void foo() { std::cout << "From class foo!\n"; } }; struct B {}; void foo(B) { std::cout << "From standalone foo!\n"; } 现在,我们需要以某种方式调用不同的函数,具体取决于foo. 这里我们面临第一个问题:如果是17岁,那么很可能我们可以这样写: A a; if constexpr (HasFoo_v<A>) { a.foo(); } else { foo(a); } 但是我们还有 2016 年,所以我们必须走另一条路:编写我们自己的 constexpr if(至少是一种可悲的外表)。这是我想出的实现: template<bool B, typename F> struct ConditioanlCall { ConditioanlCall(F function) {} }; template<typename F> struct ConditioanlCall<true, F> { ConditioanlCall(F function) { auto ignore = false; function(ignore); } }; template<bool B, typename F> void callConditional(F&& function) { ConditioanlCall<B, F>{std::forward<F>(function)}; } #define CONSTEXPR_IF(condition, action) \ callConditional<condition>([&](auto) action); 它是这样使用的: int main() { A a; B b; CONSTEXPR_IF(!HasFoo_v<B>, {foo(b);}); CONSTEXPR_IF(HasFoo_v<A>, {a.foo();}); return 0; } 完整的代码可以在链接中找到。
这是这样做的:
现在谈谈这里发生的事情。该代码使用了一个名为 SFINAE 的成语。缩写 SFINAE 代表替换失败不是错误,意思如下:定义函数重载时,错误的模板实例化不会导致编译错误,但会从最合适的重载候选列表中丢弃。
现在让我们看看这段代码的行为。该类
Test定义了两种类型True和False。他们的重要属性是sizeof(False) != sizeof(True<T>).我们还有两种方法可供使用
detect。第一个接受指向 的指针T,第二个接受任意数量的参数(省略号)。省略号是杂食性的,同时在选择重载时优先级最低。调用时,
detect(static_cast<T*>(0)编译器会尝试找到合适的重载。模板版本具有更高的优先级detect。但是如果类型T没有方法void foo(),那么模板的实例化就会失败。但是我们记得替换失败不是错误。编译器将优先使用省略号(点)。detect我们可以通过返回值的类型找出选择了哪个版本。我们可以通过大小来区分类型。请注意,代码中没有方法的实现
detect。sizeof它利用了它不评估表达式的值这一事实,它立即提取类型。因此这些方法detect将永远不会被调用。exists因此,如果true类型T有方法,它将在变量中void foo()。所有代码都在编译时执行,不提供任何开销。
另外,这段代码是由在线编译器以C++98兼容模式编译的
UPD:我在评论中暗示这不能解决你的问题。在某种程度上,这是事实。结果值不能简单地用在
if-e 中,因为这会导致编译错误。我们需要一个if可以在编译阶段工作的类似物。模板专业化可以像这样if-a:该结构
Foo有两个专业。第一个是为了Test<T>::exists == true,第二个是为了false。好吧,为了方便起见,蛋糕上的小樱桃,除了自动推断参数类型外,没有添加任何新内容:现在
main它看起来像这样:完整示例
以下所有内容都适用于 C++,至少版本 14。你基本上有两个问题:第一个是确定类是否有函数,第二个是根据是否有函数来调用函数。第一个问题很简单地解决了:
它是这样使用的:
假设我们有以下输入:
现在,我们需要以某种方式调用不同的函数,具体取决于
foo. 这里我们面临第一个问题:如果是17岁,那么很可能我们可以这样写:但是我们还有 2016 年,所以我们必须走另一条路:编写我们自己的 constexpr if(至少是一种可悲的外表)。这是我想出的实现:
它是这样使用的:
完整的代码可以在链接中找到。