2

I have a templated class called BaseSignal,

  template <class T>
  class BaseSignal
  {
    public:
    // Constructor
    BaseSignal(long buf_size, bool is_async, SimData *sim)
    ....

From this class are derived two other templated classes, Net and Reg. (For those interested, I am modeling the behavior of concurrent and sequential assignments in Verilog). These are defined as

  template <class T>
  class Net : public BaseSignal<T>
  {
    public:
    Net(long buf_size, bool is_async, SimData* sim)
      : BaseSignal<T>(buf_size, is_async, sim) {};
      ....

and similarly for Reg.

In the base class, I have defined the Set and Get methods as virtual,

    // Virtual settors
    virtual void Set(long val) {};
    virtual void Set(double val) {};

    // Virtual gettors
    virtual void Get(long* val) const {};
    virtual void Get(double* val) const {};

As the Net and Reg have different behaviors for these. So here's the interesting part.

+= Overloading Works in the Base Class

In the base class, I define this operator to call the virtual Set and Get operations, which works as expected.

In the BaseSignal:

    BaseSignal<T>& operator+=(const double& rhs)
    {
      T rval;
      this->Get(&rval);
      this->Set(rval + static_cast<T>(rhs));
      return *this;
    }
    BaseSignal<T>& operator+=(const long& rhs)
    {
      T rval;
      this->Get(&rval);
      this->Set(rval + static_cast<T>(rhs));
      return *this;
    }

In my code, I have pointers net_real and net_int and when I do

  *net_real += 1.1;
  *net_int += 1l;

The net values increment correctly. Here's the weird part

= Overloading Is Not Working in the Base Class

Overloading = in the Net class works fine, as expected:

    Net<T>& operator=(const double& rhs)
    {
      this->Set(static_cast<T>(rhs));
      return *this;
    }
    Net<T>& operator=(const long& rhs)
    {
      this->Set(static_cast<T>(rhs));
      return *this;
    }

But if I put this in BaseSignal,

    BaseSignal<T>& operator=(const double& rhs)
    {
      this->Set(static_cast<T>(rhs));
      return *this;
    }
    BaseSignal<T>& operator=(const long& rhs)
    {
      this->Set(static_cast<T>(rhs));
      return *this;
    }

I can compile the class file fine, but when I compile main.cpp, I get this:

ctests/main.cpp: In function 'int main()':
ctests/main.cpp:28:15: error: no match for 'operator=' (operand types are 'cpysim::Net<double>' and 'double')
   28 |   *net_real = 1.0;
      |               ^~~
In file included from ctests/main.cpp:9:
csrc/signals_nets.hpp:456:9: note: candidate: 'constexpr cpysim::Net<double>& cpysim::Net<double>::operator=(const cpysim::Net<double>&)'
  456 |   class Net : public BaseSignal<T>
      |         ^~~
csrc/signals_nets.hpp:456:9: note:   no known conversion for argument 1 from 'double' to 'const cpysim::Net<double>&'
csrc/signals_nets.hpp:456:9: note: candidate: 'constexpr cpysim::Net<double>& cpysim::Net<double>::operator=(cpysim::Net<double>&&)'
csrc/signals_nets.hpp:456:9: note:   no known conversion for argument 1 from 'double' to 'cpysim::Net<double>&&'

I'm not 100% sure I understand the "candidate" part. Is it trying to call the copy constructor? And I also don't get why there is no match with operands (operand types are 'cpysim::Net<double>' and 'double'), since Net is derived from BaseSignal, and I have defined the operator for these operands. Even more puzzling is why it works for += and not =.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Colin Weltin-Wu
  • 347
  • 1
  • 2
  • 10

1 Answers1

4

This is a name-hiding issue; the derived class has the implicitly-generated operator=s, including copy assignment operator and move assignment operator, which hide the operator=s of the base class.

That's how name lookup works. When the name operator= is found at the derived class scope, the further scope including base class scope won't be examined, the name lookup stops. After that overload resolution is performed based on the name got found and result in compilation error at last.

operator+= doesn't have such issue; there're no (implicitly or explicitly) declared operator+= in the derived class.

You can use using to introduce the operator=s of the base class into the derived class scope. e.g.

template <class T>
class Net : public BaseSignal<T>
{
  public:
    using BaseSignal<T>::operator=;
  ...
};
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Adding just that fixed everything, thank you. In all my googling I never saw the term "name-hiding", but now that you point it out, I see it everywhere. – Colin Weltin-Wu Jun 24 '19 at 01:34
  • BTW, this is only my third post on S.O., do you (or anyone) have any comments/suggestions in how I phrase and state problems in my posts? Thanks again. – Colin Weltin-Wu Jun 24 '19 at 01:35
  • @ColinWeltin-Wu Yes, correct terms are important for solving issues. – songyuanyao Jun 24 '19 at 01:36
  • @ColinWeltin-Wu You stated the problem well; try to make a [MCVE](https://stackoverflow.com/help/minimal-reproducible-example) next time, it's quite helpful for others to understand the problem. e.g. https://wandbox.org/permlink/RyevW2jGvID1156y – songyuanyao Jun 24 '19 at 01:41
  • Whoa didn't even know that existed, thanks will do next time. – Colin Weltin-Wu Jun 24 '19 at 01:55