Search code examples
c++templatesc++17template-specializationassignment-operator

Having trouble with user defined assignment operator for class template


#include <iostream>
#include "Register.h"

int main() {
    // Basic constructors
    Reg8  r8{ 0xC4 };
    Reg16 r16{ 0x1234 };
    Reg32 r32{ 0x89ABCDEF };
    Reg64 r64{ 0xABBA04200240ABBA };

    // I can do this with similar types:
    Reg8  r8b  = r8;  // okay
    Reg16 r16b = r16; // okay
    Reg32 r32b = r32; // okay
    Reg64 r64b = r64; // okay

    Reg8  r8c;
    r8c  = r8;  // okay
    Reg16 r16c;
    r16c = r16; // okay
    Reg32 r32c;
    r32c = r32; // okay
    Reg64 r64c;
    r64c = r64; // okay

    // Attempting with different Register Types.
    // Now Fixed by doing the following:
    // Reg16 r16d = r8; // Not okay - need to static_cast it.
    Reg16 r16d = static_cast<Reg16>(r8); // okay

    // What I would like to do:
    Reg16 r16e;
    r16e = r32; // not okay:

    // NOTE: You can see some of my attempts of overloading
    // specializing the operator=() below the class section.

    return EXIT_SUCCESS;
}

Register.h

#pragma once

#include <algorithm>
#include <bitset>
#include <cassert>
#include <climits>
#include <cstdint>
#include <iterator>
#include <iostream>
#include <iomanip>
#include <limits>
#include <map>
#include <string>
#include <type_traits>

namespace vpc {
    using u8  = std::uint8_t;
    using u16 = std::uint16_t;
    using u32 = std::uint32_t;
    using u64 = std::uint64_t;

    template<typename T>
    struct Register;

    using Reg8  = Register<u8>;
    using Reg16 = Register<u16>;
    using Reg32 = Register<u32>;
    using Reg64 = Register<u64>;

    template<typename T>
    struct Register {
        T value;
        T previous_value;
        std::bitset<sizeof(T)* CHAR_BIT> bits;

        Register() : value{ 0 }, previous_value{ 0 }, bits{ 0 } {}

        template<typename V, std::enable_if_t<(sizeof(V) > sizeof(T))>* = nullptr>
        explicit Register(const V val, const u8 idx = 0) :
            value{ static_cast<T>((val >> std::size(bits) * idx) &
                  std::numeric_limits<std::make_unsigned_t<T>>::max()) },
            previous_value{ 0 },
            bits{ value }
        {
            constexpr u16 sizeT = sizeof(T);
            constexpr u16 sizeP = sizeof(V);
            assert((idx >= 0) && (idx <= ((sizeP / sizeT) - 1)) );
        }

        template<typename V, std::enable_if_t<(sizeof(V) < sizeof(T))>* = nullptr>
        explicit Register(const V val, const u8 idx = 0) :
            value{ static_cast<T>((static_cast<T>(val) << sizeof(V)*CHAR_BIT*idx) &
                  std::numeric_limits<std::make_unsigned_t<T>>::max()) },
            previous_value{ 0 },
            bits{ value }
        {
            constexpr u16 sizeT = sizeof(T);
            constexpr u16 sizeP = sizeof(V);
            assert((idx >= 0) && (idx <= ((sizeT / sizeP) - 1)) );
        }

        template<typename V, std::enable_if_t<(sizeof(V) == sizeof(T))>* = nullptr>
        explicit Register(const V val, const u8 idx = 0) :
            value{ static_cast<T>( val ) }, previous_value{ 0 }, bits{ value }
        {}

        // the explicit on the copy constructor was the culprit
        template<typename V>
        /*explicit*/ Register(const Register<V>& reg, const u8 idx = 0) : Register(reg.value, idx) {}               

        Register& operator=(const Register& obj) {
            this->value = obj.value;
            this->previous_value = obj.previous_value;
            this->bits = obj.bits;
            return *this;
        }
    };      

} // namespace vpc

Here are some of the attempts at overloading - specializing the operator=(), these were inside of my class's declaration.

Reg8& operator=(const Reg16& rhs) {
    Reg8 temp{ rhs };
    this->value = temp.value;
    this->previous_value = temp.previous_value;
    this->bits = temp.bits;
    return *this;
}

Reg8& operator=(const Reg32& rhs) {
    Reg8 temp{ rhs };
    this->value = temp.value;
    this->previous_value = temp.previous_value;
    this->bits = temp.bits;
    return *this;
}

Reg8& operator=(const Reg64& rhs) {
    Reg8 temp{ rhs };
    this->value = temp.value;
    this->previous_value = temp.previous_value;
    this->bits = temp.bits;
    return *this;
}

Reg16& operator=(const Reg8& rhs) {
    Reg16 temp{ rhs };
    this->value = temp.value;
    this->previous_value = temp.previous_value;
    this->bits = temp.bits;
    return *this;
}


Reg16& operator=(const Reg32& rhs) {
    Reg16 temp{ rhs };
    this->value = temp.value;
    this->previous_value = temp.previous_value;
    this->bits = temp.bits;
    return *this;
}

Reg16& operator=(const Reg64& rhs) {
    Reg16 temp{ rhs };
    this->value = temp.value;
    this->previous_value = temp.previous_value;
    this->bits = temp.bits;
    return *this;
}

I believe there should be a way to be able to do this but I'm not sure how, and if I'm on the right path with specialization of these overloads, I don't know the proper syntax and Visual Studio is giving me this compiler error:

1>------ Build started: Project: TestRegister, Configuration: Debug x64 ------
1>main.cpp
1>c:\...\register.h(109): error C2556: 'vpc::Reg16 &vpc::Register<vpc::u8>::operator =(const vpc::Reg8 &)': overloaded function differs only by return type from 'vpc::Register<vpc::u8> &vpc::Register<vpc::u8>::operator =(const vpc::Register<vpc::u8> &)'
1>c:\...\register.h(78): note: see declaration of 'vpc::Register<vpc::u8>::operator ='
1>c:\...\main.cpp(23): note: see reference to class template instantiation 'vpc::Register<vpc::u8>' being compiled
1>c:\...\register.h(109): error C2371: 'vpc::Register<vpc::u8>::operator =': redefinition; different basic types
1>c:\...\register.h(78): note: see declaration of 'vpc::Register<vpc::u8>::operator ='
1>c:\...\register.h(85): error C2556: 'vpc::Reg8 &vpc::Register<vpc::u16>::operator =(const vpc::Reg16 &)': overloaded function differs only by return type from 'vpc::Register<vpc::u16> &vpc::Register<vpc::u16>::operator =(const vpc::Register<vpc::u16> &)'
1>c:\...\register.h(78): note: see declaration of 'vpc::Register<vpc::u16>::operator ='
1>c:\...\register.h(110): note: see reference to class template instantiation 'vpc::Register<vpc::u16>' being compiled
1>c:\...\register.h(85): error C2371: 'vpc::Register<vpc::u16>::operator =': redefinition; different basic types
1>c:\...\register.h(78): note: see declaration of 'vpc::Register<vpc::u16>::operator ='
1>c:\...\register.h(86): error C2079: 'vpc::Register<vpc::u16>::temp' uses undefined struct 'vpc::Register<vpc::u8>'
1>c:\...\register.h(87): error C2059: syntax error: 'this'
1>c:\...\register.h(87): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(88): error C2059: syntax error: 'this'
1>c:\...\register.h(88): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(89): error C2059: syntax error: 'this'
1>c:\...\register.h(89): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(90): error C2059: syntax error: 'return'
1>c:\...\register.h(90): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(93): error C2143: syntax error: missing ';' before ''symbol''
1>c:\...\register.h(111): error C2059: syntax error: 'this'
1>c:\...\register.h(111): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(112): error C2059: syntax error: 'this'
1>c:\...\register.h(112): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(113): error C2059: syntax error: 'this'
1>c:\...\register.h(113): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(114): error C2059: syntax error: 'return'
1>c:\...\register.h(114): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(117): error C2143: syntax error: missing ';' before ''symbol''
1>c:\...\main.cpp(29): error C2679: binary '=': no operator found which takes a right-hand operand of type 'vpc::Reg8' (or there is no acceptable conversion)
1>c:\...\register.h(78): note: could be 'vpc::Register<vpc::u16> &vpc::Register<vpc::u16>::operator =(const vpc::Register<vpc::u16> &)'
1>c:\...\main.cpp(29): note: while trying to match the argument list '(vpc::Reg16, vpc::Reg8)'
1>c:\...\register.h(101): error C2556: 'vpc::Reg8 &vpc::Register<vpc::u64>::operator =(const vpc::Reg64 &)': overloaded function differs only by return type from 'vpc::Register<vpc::u64> &vpc::Register<vpc::u64>::operator =(const vpc::Register<vpc::u64> &)'
1>c:\...\register.h(78): note: see declaration of 'vpc::Register<vpc::u64>::operator ='
1>c:\...\main.cpp(141): note: see reference to class template instantiation 'vpc::Register<vpc::u64>' being compiled
1>c:\...\register.h(101): error C2371: 'vpc::Register<vpc::u64>::operator =': redefinition; different basic types
1>c:\...\register.h(78): note: see declaration of 'vpc::Register<vpc::u64>::operator ='
1>c:\...\register.h(103): error C2059: syntax error: 'this'
1>c:\...\register.h(103): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(104): error C2059: syntax error: 'this'
1>c:\...\register.h(104): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(105): error C2059: syntax error: 'this'
1>c:\...\register.h(105): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(106): error C2059: syntax error: 'return'
1>c:\...\register.h(106): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(109): error C2143: syntax error: missing ';' before ''symbol''
1>c:\...\register.h(93): error C2556: 'vpc::Reg8 &vpc::Register<vpc::u32>::operator =(const vpc::Reg32 &)': overloaded function differs only by return type from 'vpc::Register<vpc::u32> &vpc::Register<vpc::u32>::operator =(const vpc::Register<vpc::u32> &)'
1>c:\...\register.h(78): note: see declaration of 'vpc::Register<vpc::u32>::operator ='
1>c:\...\main.cpp(142): note: see reference to class template instantiation 'vpc::Register<vpc::u32>' being compiled
1>c:\...\register.h(93): error C2371: 'vpc::Register<vpc::u32>::operator =': redefinition; different basic types
1>c:\...\register.h(78): note: see declaration of 'vpc::Register<vpc::u32>::operator ='
1>c:\...\register.h(95): error C2059: syntax error: 'this'
1>c:\...\register.h(95): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(96): error C2059: syntax error: 'this'
1>c:\...\register.h(96): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(97): error C2059: syntax error: 'this'
1>c:\...\register.h(97): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(98): error C2059: syntax error: 'return'
1>c:\...\register.h(98): error C2238: unexpected token(s) preceding ';'
1>c:\...\register.h(101): error C2143: syntax error: missing ';' before ''symbol''
1>Done building project "TestRegister.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

I've tried many other ways, I've searched google, I've searched here looking for other Q/A's but couldn't seem to find anything relevant to my situation, unless if I overlooked something. What do I have to do to get this to compile?

EDIT

Both the copy constructor and assignment operator were failing because of the overlooked explicit on the Copy Constructor. This was giving me the plethora of visual studio compiler errors. Removing that explicit key word, resolved everything. And the operator=() that is in my class above is all that was needed. The overloads or specializations below the class section are not even need at all. Over 3 hours of trying to figure this out...


Solution

  • If I understand correctly your code, an operator=() for different Register<T> types is simple

        template <typename U>
        Register & operator= (Register<U> const & obj)
         { return *this = Register{obj}; }
    

    But when you write

        Reg16 r16c = r8; 
    

    you're calling a contructor, not operator=().

    It seems to me that you have to remove the explicit in

        template<typename V>
        explicit Register(const Register<V>& reg, const u8 idx = 0) : Register(reg.value, idx) {}
    

    You have also to correct the main operator=() because obj is a reference, not a pointer; so you have to use point operator, not arrow operator

        Register& operator=(const Register& obj) {
            this->value = obj.value; // not obj->value
            this->previous_value = obj.previous_value; // not obj->previous_value
            this->bits = obj.bits; // not obj->bits
            return *this;
        };
    

    En passant: your main operator=() isn't equal to thr default operator=()?