Home > Net >  Deducing type from template class conversion operator
Deducing type from template class conversion operator

Time:02-23

I'm debugging an issue in a large C codebase where an attribute of a struct is occasionally being changed to a bad value. Unfortunately, the attribute is public and is accessed or changed in hundreds of places, so simply adding a breakpoint on a mutator is not possible. Also, I don't know the instance of the struct, so adding an address watchpoint wouldn't help.

Instrumenting the code would be a major job. However, a colleague helpfully suggested creating a proxy class which could wrap the existing type in the struct declaration. For example, instead of using MyType _type I would replace this with ChangeProxy<MyType> _type in the struct and the application should compile and work with the proxy taking the place of the direct type in the same manner as, for example, a smart pointer.

However, when I build an example, the implicit conversion operation in the template class doesn't appear to get invoked in type deduction. Here's the code:

#include <iostream>

class MyType {
    long _n = 0;

    public:
        MyType() {}
        MyType(const long n) : _n{n} {}
        MyType& operator=(const long n) { _n = n; return *this; }
        bool isZero() const { return _n != 0; }
};

template <class T>
class ChangeProxy {
    public:
        ChangeProxy() {}
        ChangeProxy(const T& t) : _t{t} {}
        ChangeProxy(const T&& t) : _t{std::move(t)} {}
        ChangeProxy& operator=(const T& t) {onChange(t); _t = t; return *this;}
        ChangeProxy& operator=(const T&& t) {onChange(t); _t = std::move(t); return *this;}
        operator T() {return _t;}
    private:
        T _t;
        void onChange(const T& newVal) { /* something here to notify me of changes */ };
};

struct MyStruct {
    // MyType _type;              // this works ...
    ChangeProxy<MyType> _type;    // .. but this doesn't
};

int main() {

    MyStruct i;
    std::cout << "i._type.isZero() : " << std::boolalpha << i._type.isZero() << std::endl;
    i._type = 1;
    std::cout << "i._type.isZero() : " << std::boolalpha << i._type.isZero() << std::endl;

    return 0;
}

Unfortunately, when I build this I get the following errors:

proxy-variable~test.cpp:35:73: error: ‘class ChangeProxy<MyType>’ has no member named ‘isZero’
   35 |         std::cout << "i._type.isZero() : " << std::boolalpha << i._type.isZero() << std::endl;
      |                                                                         ^~~~~~
proxy-variable~test.cpp:37:73: error: ‘class ChangeProxy<MyType>’ has no member named ‘isZero’
   37 |         std::cout << "i._type.isZero() : " << std::boolalpha << i._type.isZero() << std::endl;
      |                                                                         ^~~~~~

So it seems that the compiler isn't deducing that it can cast a ChangeProxy<MyType> to a MyType. What have I done wrong here?

CodePudding user response:

The context here doesn't let the compiler try out implicit conversions. Calling a member function on some object never does. You can force this by e.g.

std::cout << "i._type.isZero() : " << std::boolalpha <<
         static_cast<MyType>(i._type).isZero() << '\n';
//       ^^^^^^^^^^^^^^^^^^^ Here, enforce conversion

Another option would be:

MyStruct i;
const MyType& underlying = i._type; // Again, request conversion manually
std::cout << underlying.isZero() << '\n';

CodePudding user response:

What you are doing is invoking a method on the class ChangeProxy<MyType> which indeed doesn't have any method isZero() defined on it, hence the compilation error. You could probably add something like

T const& operator()() const {return _t;}

And then call it using

i._type().isZero()
  • Related