简体   繁体   English

C++ 可变模板移除函数逻辑

[英]C++ Variadic Template Remove Function Logic

I've been able to progress further with my variadic template from my previous question .我已经能够使用我之前的问题中的可变参数模板进一步取得进展。 I've now got a new question.我现在有一个新问题。 In this code example:在此代码示例中:

#include <iostream>
#include <cstddef>

constexpr std::uint32_t Flag0   = 0x0001;
constexpr std::uint32_t Flag1   = 0x0002;
constexpr std::uint32_t Flag2   = 0x0004;
constexpr std::uint32_t FlagAll = 0xFFFF;

template<std::uint32_t...Cs>
struct flags_tag {constexpr flags_tag(){}; };

template<std::uint32_t...Cs>
struct make_flags{ using type=flags_tag<Cs...>; };
template<std::uint32_t...Cs>
using make_flags_t=typename make_flags<Cs...>::type;

template<std::uint32_t value>
class pValue_t
{
    template<std::uint32_t StateMask, class flags>
    friend class Compound;    
};

template<> class pValue_t<Flag0>
{ 
public:
    pValue_t() : 
        m_pValue0(reinterpret_cast<void*>(0xFFFFFFFF))
    {} 

protected:
    void* m_pValue0;
};

template<> class pValue_t<Flag1>
{ 
public:
    pValue_t() : 
        m_pValue1(reinterpret_cast<void*>(0xDEADBEEF))
    {}

protected:
    void* m_pValue1;
};

template<> class pValue_t<Flag2>
{ 
public: 
    pValue_t() : 
        m_pValue2(reinterpret_cast<void*>(0xCAFEBABE))
    {}

protected:
    void* m_pValue2;
};

template<std::uint32_t StateMask, class flags>
class Compound;

template<std::uint32_t StateMask, std::uint32_t...Cs>
class Compound< StateMask, flags_tag<Cs...> >:
  public pValue_t<Cs>...
{       
public:
    void print()
    { 
        if (IsStateValid(Flag0))
        { 
            std::cout << this->m_pValue0 << '\n';
        }

        if ((StateMask & Flag1) == Flag1)
        {
            std::cout << this->m_pValue1 << '\n';
        }

        // *** THIS IS THE PROBLEM STATEMENT ***
        if (IsStateValid(Flag2))
        {
            std::cout << this->m_pValue2 << '\n';
        }

    }

    static bool IsStateValid(std::uint32_t stateMask)
        { return ((StateMask & stateMask) == stateMask); }

    uint32_t m_stateMask;
};

using my_type = Compound< Flag0 | Flag1, make_flags_t<Flag0, Flag1>>;

int main() {
  my_type test;
  test.print();
}

the print function contains a reference to m_pValue2 , which is valid when the StateMask contains Flag2 . print函数包含对m_pValue2的引用,当StateMask包含Flag2时,该引用有效。

Now, the compiler is warning that it cannot find m_pValue2 .现在,编译器警告它找不到m_pValue2 I would like for the compiler to remove the chunk of code that references m_pValue2 when the StateMask (known at compile time) does not contain Flag2 (when IsStateValid() is false).StateMask (在编译时已知)不包含Flag2 (当IsStateValid()为假时)时,我希望编译器删除引用m_pValue2的代码块。

The exact error is as follows:确切的错误如下:

main.cpp: In instantiation of 'void Compound<StateMask, flags_tag<Cs ...> >::print() [with unsigned int StateMask = 3u; unsigned int ...Cs = {1u, 2u}]':
main.cpp:95:18:   required from here
main.cpp:80:27: error: 'class Compound<3u, flags_tag<1u, 2u> >' has no member named 'm_pValue2'
             std::cout << this->m_pValue2 << '\n';

I'm hoping this is possible.我希望这是可能的。 In other template programming, I've used IsStateValid() to compile out code segments that don't match the StateMask .在其他模板编程中,我使用IsStateValid()编译出与StateMask不匹配的代码段。 I've never tried to compile away a possibly missing member variable, however.但是,我从未尝试过编译掉可能丢失的成员变量。

Does anyone have any ideas?有没有人有任何想法?

Why it doesn't work为什么它不起作用

All branches in a function template will be compiled regardless of type.无论类型如何,函数模板中的所有分支都将被编译。 It doesn't matter that IsStateValid(Flag2) would be false at compile time, the body of that if must be valid code.这不要紧, IsStateValid(Flag2)false ,在编译时的身体if必须是有效的代码。 As there is no this->m_pValue2 in that case, this is a hard error.由于在这种情况下没有this->m_pValue2 ,这是一个硬错误。

What can you do to fix it你能做些什么来修复它

You need to forward each print flag function to a function template that will either print the value (if it exists) or do nothing (if it doesn't).您需要将每个打印标志函数转发到一个函数模板,该模板将打印值(如果存在)或什么都不做(如果不存在)。 We can use function overloading to help here, and ensure that the entire function will not be instantiated if there is no such flag.我们可以使用函数重载来帮助这里,并确保如果没有这个标志,整个函数不会被实例化。 For example:例如:

void print()
{
    printImpl<Flag0>();
    printImpl<Flag1>();
    printImpl<Flag2>();
}

template <uint32_t F>
void printImpl() {
    printImpl<F>(std::is_base_of<pValue_t<F>, Compound>{});
}

template <uint32_t F>
void printImpl(std::true_type ) {
    // we DO have this flag
    pValue_t<F>::print();
}

template <uint32_t F>
void printImpl(std::false_type ) {
    // we do NOT have this flag
    // so do nothing
}

All you need to do at this point is add the appropriate print() seg:此时您需要做的就是添加适当的print()段:

template<> class pValue_t<Flag2>
{ 
public: 
    pValue_t() : 
        m_pValue2(reinterpret_cast<void*>(0xCAFEBABE))
    {}

    void print() {
        std::cout << m_pValue2 << '\n';
    }

protected:
    void* m_pValue2;
};

I got this to work ( working example ), but it seems very hacky.我让它工作(工作示例),但它似乎很hacky。 It shows that it's possible - hopefully a more experienced person than I can clean it up.它表明这是可能的 - 希望一个比我更有经验的人可以清理它。

The idea is to make IsStataValid a constexpr and separate the code in question out into another function, which has two variants.这个想法是使IsStataValid成为constexpr并将有问题的代码分离到另一个函数中,该函数有两个变体。 The variant that gets instantiated depends on the compile-time flag:实例化的变体取决于编译时标志:

static constexpr bool IsStateValid(std::uint32_t stateMask)
    { return ((StateMask & stateMask) == stateMask); }

template <typename A = void,
          typename T = typename std::enable_if<IsStateValid(Flag2), A>::type>
void blub(int x=0) {
    std::cout << this->m_pValue2 << '\n';
}

template <typename A = void, 
          typename T = typename std::enable_if<!IsStateValid(Flag2), A>::type>
void blub(long x=0) {
}

Then instead of the if statement in print() you call the helper function:然后调用辅助函数而不是print()中的if语句:

blub();

typename A is a dummy parameter to make the enable_if dependent on a template parameter so SFINAE can kick in. The blub s take a first parameter of a different type so the compiler doesn't complain about it not being able to be overloaded. typename A是一个虚拟参数,使enable_if依赖于模板参数,因此 SFINAE 可以启动。 blub s采用不同类型的第一个参数,因此编译器不会抱怨它无法重载。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM