RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 579570
Accepted
pank
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 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. 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
    }
    

    完整示例

    • 14
  2. 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;
    }
    

    完整的代码可以在链接中找到。

    • 7

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5