Search code examples
c++templatesc++11bit-fieldsperfect-forwarding

Perfect forwarding workaround for bit-fields


I’m looking for a workaround for bit-field in overload resolution for template.

I have a function that I templated for perfect forwarding of its arguments:

template <typename... Args> void f(Args &&...args) { }

If I try to use it with a bit-field argument, like this:

struct bits { unsigned int foo:1; };
bits b{1};
f(b.foo);

…it fails to compile:

main.cpp:26:7: error: non-const reference cannot bind to bit-field 'foo'
    f(b.foo);
      ^~~~~

Is there a way to overload f() such that it takes bit-fields by value but still takes other arguments by reference in the common case?

So far I haven't been able to. For instance, if I add an overload that takes arguments by value…

main.cpp:27:5: error: call to 'f' is ambiguous
    f(b.foo);
    ^

Solution

  • http://coliru.stacked-crooked.com/view?id=b694c6cc3a52e0c14bedd6a26790d99d-e54ee7a04e4b807da0930236d4cc94dc

    It can be done, if poorly. I recommend not doing this. Basically, the key part is since you can't have a pointer or a reference to a bitfield, you instead use a lambda which sets the bitfield for you.

    I dislike macros as much as the next guy, but it's the only way I could think of to avoid requiring callers to put in a lambda at the callsite.

    template<class assigner_type>
    struct bitfieldref_type {
        bitfieldref_type(bool value, assigner_type&& assign) :value(value), assign(std::move(assign)) {}
        operator bool() const {return value;}
        bitfieldref_type& operator=(bool v) {assign(v); value=v; return *this;}
    private:
        bool value;
        assigner_type assign;
    };
    template<class assigner_type>
    bitfieldref_type<assigner_type> make_bitfieldref(bool value,  assigner_type&& assign)
    {return {value, std::move(assign)};}
    //macro is optional
    #define bitfieldref(X) make_bitfieldref(X, [&](bool v)->void{X=v;})
    

    usage:

    template <class T, typename... Args> void proof_it_works(T&& first) 
    {first = 0;}
    template <class T, typename... Args> void proof_it_works(T&& first, Args &&...args) {
        first = 0;
        proof_it_works(std::forward<Args>(args)...);
    }    
    template <typename... Args> void f(Args &&...args) {proof_it_works(std::forward<Args>(args)...);}
    
    int main() {
        struct bits { unsigned int foo:1; };
        bits b{1};
        int a = -1;
        float c = 3.14;
        f(a, bitfieldref(b.foo), c);
        std::cout << a << b.foo << c;
        return 0;
    }
    

    I just noticed that my bitfieldref_type assumes the value is a bool, instead of a unsigned int, but I'll leave fixing that as an excersize for the user.