Можно ли сделать так, чтобы `=` предпочитало присвоение из преобразования вместо (удаленного) копирования?

Я обнаружил несколько потоков, которые в значительной степени подразумевают, что это невозможно, но ни один из них не использует точно такую ​​же комбинацию операторов и условий, поэтому я хотел бы спросить более конкретно. Надеюсь, это означает, что это быстрый и простой ответ для кого-то … так или иначе!

Рассмотрим пример прокси-класса, созданный для управления значением в большем блоке хранилища — как в этом упрощенном, но репрезентативном примере:

class SomeProxyThing {
std::uint32_t storage;

public:
operator std::uint16_t() const
{
return storage & 0x0000FFFF;
}

SomeProxyThing &operator=(std::uint16_t const value)
{
storage &= 0xFFFF0000;
storage |= value;
}
};

я хочу все назначения для работы через пользовательский operators. В этом случае пользователь должен иметь возможность только передавать или убирать «открытый» тип std::uint16_t, Возможно, я использую различные типы прокси-классов и хочу, чтобы это применялось ко всем из них. В идеале, для любой комбинации типов, я мог бы просто напечатать someProxy = anotherProxy и пусть компилятор сделает все остальное.

Но когда левая и правая части назначения имеют одинаковые типы или типы, связанные с наследованием, оператор назначения копирования по умолчанию — конечно, — вступает в противоречие с этой целью. Копирует весь storageтаким образом, заглушая вторую половину этого uint32_t — вместо того, чтобы копировать только «выставленное» значение по желанию. И это правильно! Для большинства случаев. Но я хотел бы получить способ «присвоения по конверсии», даже если типы LHS и RHS совпадают. Чтобы избежать этого, я могу:

  • переопределить оператор присваивания копии выполнить «прокси» копию, используя пользовательский operators — это то, чем я занимаюсь а также, как любой пользовательский конструктор / оператор присваивания, ломает тривиально копируемый статус struct — который мне нужно сохранить. Это еще memcpy()s тем не мение в g++, но я хочу определенного поведения.
  • или же = delete оператор копирования-назначения (что мы можем теперь сделать для типов TC). Но назначения все еще пытаются использовать это и выдают ошибку компиляции — так как delete означает «прервать с ошибкой, если я выбрал перегрузку», а не «исключить меня из разрешения перегрузки». Чтобы обойти это, я должен явно указать компилятору использовать оператор преобразования и назначим из его результата:
SomeProxyThing a, b;
a = 42;
b = static_cast<std::uint16_t>(a);
// a.k.a.
b.operator=( a.operator std::uint16_t() );

Кажется, нет способа сказать компиляторуигнорируйте любую ошибку, вызванную вашей предпочтительной перегрузкой, и выберите следующую лучшую». Есть? В более общем смысле, есть ли способ / взломать / ужасающий клудж в такой ситуации, чтобы заставить компилятор автоматически использовать / предпочитать определенный operators?

Другими словами, в идеале, в

SomeProxyThing a, b;
a = 42;
b = a;

тот b = a; действительно сделал бы это:

b = static_cast<std::uint16_t>(a);
// a.k.a.
b.operator=( a.operator std::uint16_t() );

без необходимости вводить это вручную, используйте static_castили реализовать именованные методы get / set. В идеале я хочу, чтобы операции чтения / записи для любого такого прокси-сервера выглядели точно так же, как операции чтения / записи для базовых типов в написанном коде, все с использованием =,

Я сильно подозреваю, что это невозможно … но подтверждение было бы неплохо!

5

Решение

Вы можете сделать это:

#include <stdint.h>
#include <iostream>
#include <type_traits>

using namespace std;

class Proxy_state
{
protected:
uint32_t storage;
public:
// Access to the bytes
};

static_assert( is_trivially_copyable<Proxy_state>::value, "!" );

class Some_proxy_thing
: public Proxy_state
{
private:

public:
operator std::uint16_t() const
{
return storage & 0x0000FFFF;
}

auto operator=( uint16_t const value )
-> Some_proxy_thing&
{
clog << "=(uint16_t)" << endl;
storage &= 0xFFFF0000;
storage |= value;
return *this;
}

auto operator=( Some_proxy_thing const& value )
-> Some_proxy_thing&
{ return operator=( static_cast<uint16_t>( value ) ); }
};

static_assert( not is_trivially_copyable<Some_proxy_thing>::value, "!" );

auto main()
-> int
{
Some_proxy_thing    a{};
Some_proxy_thing    b{};
const Some_proxy_thing c = b;

a = c;

a = 123;
a = b;
}

Здесь выводятся все три назначения (в стандартный поток ошибок) =(uint16t),

1

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

Соответствие / несоответствие типов времени компиляции можно контролировать с помощью std :: enable_if.
Неявное преобразование типов можно отключить с помощью явного ключевого слова.
Все конструкторы копирования и перемещения могут быть явно удалены, чтобы избежать копирования, а конструктор по умолчанию может быть явно помечен как стандартный.
Редактировать: ранний ответ учитывает первую часть вопроса о том, что «пользователь должен иметь возможность только передать или получить« открытый »тип», поэтому все преобразования должны быть явными, однако для завершения ответа вы можете определить тривиально копируемое класс и использовать это внутри вашего прокси-класса
Может быть, то, что вы хотели:

#include  <cstdint>
#include <type_traits>
struct copyable{
std::uint32_t number = 0x0;
};
class SomeProxyThing {

public:
explicit operator  std::uint16_t()  const
{
return storage.number & 0x0000FFFF;
}
template <typename T, typename std::enable_if<std::is_same<T, std::uint16_t>::value, int>::type=0>
SomeProxyThing& operator=(T value)
{
storage.number &= 0xFFFF0000;
storage.number |= value;
return *this;
}

SomeProxyThing()=default;
SomeProxyThing(const SomeProxyThing&)=delete;
SomeProxyThing(SomeProxyThing&&)=delete;
SomeProxyThing& operator=(const SomeProxyThing& other) {
this->storage.number = static_cast<std::uint16_t>(other.storage.number);
}
SomeProxyThing& operator=(SomeProxyThing&& other) {
this->storage.number = static_cast<std::uint16_t>(other.storage.number);
}

private:
copyable storage;
};
int main()
{
SomeProxyThing a, b;
a = static_cast<std::uint16_t>(43);
b = a;
}
-1

По вопросам рекламы [email protected]