Question

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);
    ^
Was it helpful?

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.

OTHER TIPS

It cannot be done (at least not how you tried it) because the Standard says so (bold emphasis mine):

13.3.3.1.4 Reference binding [over.ics.ref]

4 Other restrictions on binding a reference to a particular argument that are not based on the types of the reference and the argument do not affect the formation of a standard conversion sequence, however. [Example: a function with an “lvalue reference to int” parameter can be a viable candidate even if the corresponding argument is an int bit-field. The formation of implicit conversion sequences treats the int bit-field as an int lvalue and finds an exact match with the parameter. If the function is selected by overload resolution, the call will nonetheless be ill-formed because of the prohibition on binding a non-const lvalue reference to a bit-field (8.5.3). — end example ]

This explains why

  • the original example fails to compile, because the reference cannot bind to a bit-field
  • adding an overload template<typename... Arg> f(Args.. args) gave you the ambiguity: overload resoution ended in a tie, and the reference-binding-to-bitfield prohibition never came into play.

This is the best answer I can come up with:

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

struct bits { unsigned int foo:1; };

template <typename T> const T constipate(T v)
{ return(static_cast<const T>(v)); }

void bar()
{
bits b{1};
f(constipate(b.foo));
}

EDIT: There's an easier solution, that eliminates the need for the 'constipate' template:

void bar()
{
bits b{1};
f(b.foo + 0);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top