【问题标题】:Add extra methods at compile time according to some enum value在编译时根据一些枚举值添加额外的方法
【发布时间】:2021-01-16 20:14:17
【问题描述】:

假设我有以下课程:

class Extra final
{
public:

    void Method1() const {std::cout << "Method 1" << std::endl;}
    void Method2() const {std::cout << "Method 2" << std::endl;}
};

我为某些类T创建了一个类模板,可以根据不同的场景在编译时启用/禁用使用Extra的方法(在枚举中列出):

enum class Scenario
{
    None, // No extra.
    One,  // Only Method1 allowed.
    Two,  // Only Method2 allowed.
    Both  // Both Method1 and Method2 allowed.
};

类模板如下:

template<typename T, Scenario R>
class ExtraAdder : public T
{
};

template<typename T>
class ExtraAdder<T, Scenario::One> : public T
{
public:

    void Method1Extra() const {m_extra.Method1();}

private:

    Extra m_extra;
};

template<typename T>
class ExtraAdder<T, Scenario::Two> : public T
{
public:

    void Method2Extra() const {m_extra.Method2();}

private:

    Extra m_extra;
};

template<typename T>
class ExtraAdder<T, Scenario::Both> : public T
{
public:

    void Method1Extra() const {m_extra.Method1();}
    void Method2Extra() const {m_extra.Method2();}

private:

    Extra m_extra;
};

可以这样使用:

class Base
{
public:

    virtual ~Base() = default;
    void BaseMethod() const {std::cout << "Base method" << std::endl;}

    // Some other stuff...
};

int main(int argc, char **argv)
{
    std::cout << "Scenario: None" << std::endl;
    ExtraAdder<Base, Scenario::None> none;
    none.BaseMethod();
    //none.Method1Extra();   // Does not compile.
    //none.Method2Extra();   // Does not compile.

    std::cout << std::endl << "Scenario: One" << std::endl;
    ExtraAdder<Base, Scenario::One> one;
    one.BaseMethod();
    one.Method1Extra();
    //one.Method2Extra();    // Does not compile.

    std::cout << std::endl << "Scenario: Two" << std::endl;
    ExtraAdder<Base, Scenario::Two> two;
    two.BaseMethod();
    //two.Method1Extra();    // Does not compile.
    two.Method2Extra();

    std::cout << std::endl << "Scenario: Both" << std::endl;
    ExtraAdder<Base, Scenario::Both> both;
    both.BaseMethod();
    both.Method1Extra();
    both.Method2Extra();

    return 0;
}

关键是通过更改模板参数Scenario,我只能访问包装的m_extra 成员的某些方法,这正是我想要的。但是,如您所见,不同的专业非常冗长(并且有一些重复)。

问题:有没有一种方法可以生成完全相同的模板特化,但以一种不那么冗长的方式,有限制或没有重复?

我发现最接近解决此问题的是this question(使用std::enable_if),但我无法根据我的情况调整它。

我正在使用带有 C++17 标准的 g++。

【问题讨论】:

  • 添加的方法之间是否没有共享公共状态(即class Extra在您的实际代码中为空)?

标签: c++ templates template-specialization


【解决方案1】:

或者,您可以将enable_if 放入模板中,而不是返回类型声明。我个人觉得这更易读,它的优点是可以使用返回类型推导。

template<typename T, Scenario R>
class ExtraAdder : public T {
    Extra m_extra;
public:
    template<
        Scenario S = R, 
        typename = std::enable_if_t<(S == Scenario::One || S == Scenario::Both)>
    >
    void Method1Extra() const {
        m_extra.Method1();
    }

    
    template<
        Scenario S = R, 
        typename = std::enable_if_t<(S == Scenario::Two || S == Scenario::Both)>
    >
    decltype(auto) Method2Extra() const {
       return m_extra.Method2();
    }

};

现场示例here

enable_if 与成员函数一起使用的一个常见缺陷是,它仅在模板参数的替换格式不正确时才有效。因此,我们需要添加额外的模板参数Scenario S = R(或另一个答案中的bool),以便将替换推迟到使用该方法的位置。否则不会有这样的替换导致硬错误。请参阅this question 进行更深入的讨论。

【讨论】:

    【解决方案2】:

    您可以使用enable_if,但您还需要使成员函数成为模板函数,以便检查可以依赖于某些东西:

    template<typename T, Scenario R>
    class ExtraAdder : public T {
        Extra m_extra;
    public:
        template<bool B = true>
        std::enable_if_t<B && (R == Scenario::One || R == Scenario::Both)>
        Method1Extra() const {
            m_extra.Method1();
        }
    
        template<bool B = true>
        std::enable_if_t<B && (R == Scenario::Two || R == Scenario::Both)>
        Method2Extra() const {
            m_extra.Method2();
        }
    };
    

    如果您不需要 SFINAE 了解这些方法是否存在,您可以简单地static_assert

    template<typename T, Scenario R>
    class ExtraAdder : public T {
        Extra m_extra;
    public:
        void Method1Extra() const {
            static_assert(R == Scenario::One || R == Scenario::Both);
            m_extra.Method1();
        }
    
        void Method2Extra() const {
            static_assert(R == Scenario::Two || R == Scenario::Both);
            m_extra.Method2();
        }
    };
    

    【讨论】:

    • 或直接template &lt;Scenario S = R&gt; std::enable_if_t&lt;(S == Scenario::One) || (S == Scenario::Both)&gt;,避免B
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-15
    相关资源
    最近更新 更多