Избежание неоднозначности в разрешении перегрузки - PullRequest
0 голосов
/ 06 мая 2019

Это вопрос до этого вопроса, поэтому, если вам нужно увидеть класс Register, обратитесь к этому вопросу. Теперь, основываясь на предоставленном ответе, я написал функцию для этого. У меня есть 2 версии функции: одна будет сохранять результаты обратно в оригинал, а вторая - копию. Вот мои функции:

template<std::uint64_t N>
void reverseBitOrder( Register<N>& reg ) {
    auto str = reg.register_.to_string();
    std::reverse(str.begin(), str.end());
    auto x = vpc::Byte(str);
    reg.register_ = x;
}

// Extra unused parameter to avoid ambiguity
template<std::uint64_t N>
Register<N> reverseBitOrder(Register<N>& reg, bool _x_ /*not used*/ ) {
    auto str = reg.register_.to_string();
    std::reverse(str.begin(), str.end());
    auto x = vpc::Byte(str);
    Register<N> temp;
    temp.register_ = x;
    return temp;
}

Первый сохраняет значение, второй возвращает копию. У меня вопрос по поводу функции 2 nd . В итоге я добавил второй параметр, который не используется, чтобы избежать неоднозначности из-за разрешения перегрузки, поскольку функции не могут быть разрешены только для возвращаемых типов. Поэтому, когда я вызываю эту функцию, мне нужно передать либо 0, 1, true, либо false в функцию, которая не имеет никакого эффекта.

В целом это само по себе не очень большое дело, однако, оно не кажется мне очень чистым и лаконичным. Есть ли другие способы добиться этого? Я предпочитаю не делать это функцией класса. Мой класс или структура регистра завершены как есть, и любые операции над регистром будут выполняться функциями, которые ссылаются на один или несколько объектов регистра.

Ответы [ 2 ]

1 голос
/ 06 мая 2019

Для этого можно использовать std::optional.

Тип шаблона функции return reverseBitOrder должен быть std::optional<vpc::Register<N>>.

Функцияшаблон должен быть изменен на:

template<std::uint64_t N>
std::optional<vpc::Register<N>> reverseBitOrder(vpc::Register<N>& reg, bool _x_ = false) {

    auto str = reg.register_.to_string();
    std::reverse(str.begin(), str.end());
    vpc::Register<N> temp;    

    if(_x_) //return copy
    {
        temp.register_ = vpc::Byte(str); //since register_ is a vpc::Byte. Generalize accordingly.
        return temp;
    }
    else //save in the same variable
    {
        reg.register_ = vpc::Byte(str);        
        return {};
    }
}

Live Demo here.

Но вам не нужно использовать std::optional,поскольку в шаблоне функции действительно нет случая «сбоя».

0 голосов
/ 06 мая 2019

Если я правильно понимаю, обе функции вычисляют x и сохраняют его в Register<N>, но одна возвращает указанный объект по значению, а другая сохраняет результат в аргументе функции reg.

Технически , это может быть сделано путем перегрузки константности путем определения этих двух функций:

template<std::uint64_t N> void reverseBitOrder( Register<N>& reg );
template<std::uint64_t N> Register<N> reverseBitOrder( Register<N> const& reg );

Хотя это технически отвечает на ваш вопрос, это было бы ужасно.Если я не ошибаюсь, реальная проблема в том, что вам нужно следующее:

// Behaviour 1: value stored in reg
reverseBitOrder(reg);

// Behaviour 2: value stored in val, reg left untouched
auto val = reverseBitOrder(reg);

Проблема в том, что используется возвращаемое значение или нет - это не то, что вы можете обнаружить изнутриfunction.


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

template<std::uint64_t N> void reverseBitOrder( Register<N> const& inputReg, Register<N>& outputReg );

Эта функция будетиспользуйте inputReg для вычисления x, затем сохраните результат в outputReg, то есть вы бы использовали его следующим образом:

// Behaviour 1: value stored in reg
reverseBitOrder(reg, reg);

// Behaviour 2: value stored in val, reg leftuntouched
reverseBitOrder(reg, val);

Теперь, если это действительно нене делайте этого за вас, есть способ получить синтаксический сахар , который вы ищете, за счет ненужной сложности и добавления конструктора к Register<N>.Это выглядело бы примерно так:

// Forward declaration
template<std::uint64_t N>
class Register;

// Proxy class
template<std::uint64_t N>
struct RegisterProxy
{
    Register<N>* reg;
    TypeOfX x;

    ~RegisterProxy()
    {
        if (reg)
        {
            reg->register = x;
        }
    }
};

// Modified Register class
template<std::uint64_t N>
class Register
{
    ...

    Register(RegisterProxy& proxy)
    {
        ...
        register = proxy.x;
        proxy.reg = nullptr;
        ...
    }

    ...

    // Define the matching assignment operator too

    ...
};

// Your function
template<std::uint64_t N>
RegisterProxy<N> reverseBitOrder( Register<N>& reg )
{
    auto str = reg.register_.to_string();
    std::reverse(str.begin(), str.end());
    auto x = vpc::Byte(str);
    return RegisterProxy{reg, x};
}

Это позволяет вам делать

// Behaviour 1: temporary object immediately destroyed and value stored in reg
reverseBitOrder(reg);

// Behaviour 2: constructor called, value stored in val
//              the temporary object's destructor doesn't do anything, reg left untouched
auto val = reverseBitOrder(reg);

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

...