Идеальный обходной путь для битовых полей

Я ищу обходной путь для битовое поле в разрешении перегрузки для шаблона.

У меня есть функция, которую я настроил для идеальной пересылки своих аргументов:

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

Если я попытаюсь использовать его с аргументом битового поля, например так:

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

… он не компилируется:

main.cpp: 26: 7: ошибка: неконстантная ссылка не может быть привязана к битовому полю 'foo'
F (b.foo);
^ ~~~~

Есть ли способ перегрузить f() так что он принимает битовые поля по значению, но все же принимает другие аргументы по ссылке в общем случае?

До сих пор я не смог. Например, если я добавлю перегрузку, которая принимает аргументы по значению …

main.cpp: 27: 5: ошибка: вызов 'f' неоднозначен
F (b.foo);
^

2

Решение

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

Это можно сделать, если плохо. Я рекомендую не делать этого. По сути, ключевая часть в том, что вы не можете иметь указатель или ссылку на битовое поле, вместо этого вы используете лямбда который устанавливает битовое поле для вас.

Мне не нравятся макросы так же, как следующему парню, но это единственный способ, который я мог придумать, чтобы не требовать от вызывающих абонентов вставлять лямбду на место вызова.

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;})

использование:

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;
}

Я просто заметил, что мой bitfieldref_type предполагает, что значение является boolвместо unsigned intНо я оставлю исправление для пользователя.

2

Другие решения

Это невозможно сделать (по крайней мере, не так, как вы это делали), потому что в Стандарте так сказано (выделено жирным шрифтом):

13.3.3.1.4 Ссылочная привязка [over.ics.ref]

4 Другие ограничения на привязку ссылки к определенному аргументу
которые не основаны на типах ссылки и аргумента
однако не влияет на формирование стандартной последовательности преобразования.
[Пример: функция с параметром «lvalue ссылка на int» может
быть жизнеспособным кандидатом, даже если соответствующий аргумент является int
битовое поле. Формирование последовательностей неявного преобразования относится к
битовое поле int как int lvalue и находит точное совпадение с
параметр. Если функция выбрана разрешением перегрузки,
вызов, тем не менее, будет плохо сформирован из-за запрета на
привязка неконстантной lvalue-ссылки к битовому полю
(8.5.3). — конец
пример ]

Это объясняет почему

  • исходный пример не может быть скомпилирован, поскольку ссылка не может быть привязана к битовому полю
  • добавление перегрузки template<typename... Arg> f(Args.. args) дал вам двусмысленность: устранение перегрузки закончилось ничьей, и запрет привязки ссылки к битовому полю никогда не вступал в игру.
2

Это лучший ответ, который я могу придумать:

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));
}

РЕДАКТИРОВАТЬ: есть более простое решение, которое устраняет необходимость в шаблоне «запор»:

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