【问题标题】:C++ Change member function definition based on template parameter valueC++根据模板参数值改变成员函数定义
【发布时间】:2017-03-05 21:36:11
【问题描述】:

是否可以根据模板参数的值有条件地在函数中编译语句?例如:

template<typename T, bool chk>
class subject
{
public:
    // the ideal case    
    void doSomething(T new_val)
    {
        if(chk)
        {
          if(new_val != val)
              //do_something only if new_val is different from val
        }
        else
        {
            //do_something even if new_val and val are equal
        }
    }


    //or if that's not possible, if chk = 0 use this method
    void doSomething(T new_val) 
    {
        //do_something even if new_val and val are equal
    }

    // and if chk = 1 use this method
    void doSomething(T new_val) 
    {
        if(new_val != val)
           //do_something only if new_val is different from val
    }

    T val;
};

catch 是基于 chk 的值,我什至不希望将语句 if(new_val!=val) 编译到函数中(因为这样使用的每个类型 T 都必须定义一个 != 运算符)。

我想这种方法的一个缺点是 foo&lt;int,0&gt;foo&lt;int,1&gt; 是不同的类,因此不可能定义一个不关心 chk 是 0 还是 1 的函数(比如 watch(foo&lt;int&gt;)) .

我正在查看的应用程序是观察者,对于某些类型,我只希望在值实际更改时通知观察者,而对于其他类型,我希望始终通知观察者(对于那些我不'不想定义一个 != 运算符)。

如果没有两个单独的类,这可能吗?

【问题讨论】:

    标签: c++ c++11 templates


    【解决方案1】:

    如果没有两个单独的类,这可能吗?

    是的,是的。如果您不想专门化您的类,以避免代码重复,您可以使用如下示例中的 sfinae 表达式:

    #include <type_traits>
    #include <iostream>
    
    template<typename T, bool chk>
    struct subject {
        template<bool trigger = chk>
        std::enable_if_t<trigger>
        doSomething(T new_val) {
            if(new_val != val) {
                std::cout << "new_val != val" << std::endl;
            } else {
                std::cout << "new_val == val" << std::endl;
            }
        }
    
        template<bool trigger = chk>
        std::enable_if_t<not trigger>
        doSomething(T new_val) {
            std::cout << "who cares?" << std::endl;
        }
    
        T val;
    };
    
    int main() {
        subject<int, true> s1{0};
        s1.doSomething(0);
        s1.doSomething(1);
        subject<int, false> s2{0};
        s2.doSomething(0);
        s2.doSomething(1);
    }
    

    想法是doSomething正确 定义是在编译时获取的,它取决于模板参数chk 的值。另一个定义只是按预期丢弃,根本不可用。
    请注意,要使 sfinae 表达式起作用,trigger 模板参数必须是成员函数模板的实际参数。这就是为什么你必须这样定义它:

    template<bool trigger = chk>
    sfinae_expression_based_on_enable_if
    doSomething(T new_val) { /* ... */ }
    

    coliru 上运行时查看它。

    【讨论】:

    • 这很棒。我看过 enable_if,但 IMO 在 cppreference.com 上的文档相当难以理解,听起来好像不太适合这个(可能是因为我不太了解 SFINAE 的工作原理)
    • 实际上,您是否介意添加一个关于 enable_if_t 为您提供的功能的简要说明,而不是仅 enable_if 以及它是如何工作的?我又看了一遍,但不够用。
    • @schrödinbug 您可以在 SO 上找到很多示例。解释得比我能做的更好!! ;-)
    【解决方案2】:

    您要查找的内容称为“模板专业化”。

    您必须专门化您的模板。如上所述定义基本模板后,您将继续并定义其专业化:

    template<typename T>
    class subject<T, true>
    {
    public:
    
       // ...
    

    然后您继续并从头开始定义整个 subject 类,对第二个模板参数为 true(或 false,如果您需要专门化)的情况进行任何您需要的更改.您可以删除内容、添加内容或完全更改它们。专门化的类可以有不同的类成员、方法或相同的类方法,但它们的工作方式完全不同。

    重要的是要了解您现在定义的是整个类,而不仅仅是不同的位。如果只需要对类的一个次要方面进行专门化,这当然会导致一堆重复的代码;因此通常需要对其进行重构,将可变位放入辅助类或函数中,并仅专门化可变位。

    即将到来的 C++17 标准有一些模板专业化的其他替代方案;但在这种情况下,专业化通常是人们学习的第一件事。因此,您应该打开您的 C++ 书籍,阅读讨论模板专业化的章节,并首先了解情况,然后再继续学习 C++17 中的新内容。

    【讨论】:

    • 我看过专业化,但正如你提到的那样,它需要大量的重复,我认为它不会允许函数像一般的论点一样接受主题(即无论是否 chk是对还是错。)我认为您必须重载该功能。唔。您指的是 C++17 中的哪些功能?
    • 你应该重读我第二段的后半部分。就 C++17 而言,我指的是constexpr if
    猜你喜欢
    • 2014-07-26
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-31
    相关资源
    最近更新 更多