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

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

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

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

Я хочу, чтобы все назначения выполнялись с помощью пользовательских operator s. Пользователь должен только иметь возможность пройти или выйти из "открытого" типа, в данном случае std::uint16_t. Я могу использовать различные типы классов прокси и хочу, чтобы это применимо ко всем из них. В идеале для любой комбинации типов я мог бы просто набрать someProxy = anotherProxy и дать компилятору все остальное.

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

  • переопределить оператор присваивания копий, чтобы выполнить "проксированную" копию с использованием пользовательского operator - это то, что я делал, но он выглядит как взломанный и, как любой пользователь -пределенный оператор конструктора/присваивания, разбивает тривиально скопируемый статус struct - который мне нужно сохранить. Он все еще memcpy() в любом случае в 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() );

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

Иными словами, в идеале, в

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
задан underscore_d 12 июля '16 в 15:12
источник поделиться

2 ответов

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

#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
ответ дан Cheers and hth. - Alf 12 июля '16 в 22:22
источник поделиться

Совпадение/несоответствие типа времени компиляции можно контролировать с помощью 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
ответ дан rahnema1 12 июля '16 в 19:53
источник поделиться

Другие вопросы по меткам