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); ^
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.
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
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With