Home > Net >  Invalid conversion on template argument type?
Invalid conversion on template argument type?

Time:07-11

With help from this question, I have gradually built up this code, a wrapper around a class member function. The idea is that I can use operator* on my property to write to the class object:

#include <cstdio>
#include <type_traits>
#include <utility>

using namespace std;


class testclass {
public:
    double get() { return d_; }
    bool set(double d) { d_ = d; return true; }
    double d_ = 0.0;
};

template <typename PropertyType>
struct DEFAULT_WRAPPER_PROXY {
    DEFAULT_WRAPPER_PROXY(PropertyType* p) : property_(p) {}

    operator typename PropertyType::GetterReturnType() {
        return property_->Get();
    }

    DEFAULT_WRAPPER_PROXY& operator=(typename PropertyType::GetterReturnType val) {
        property_->Set(val);
        return *this;
    }

    PropertyType* property_;
};

template <typename PropertyType>
struct LOGGING_WRAPPER_PROXY {
    LOGGING_WRAPPER_PROXY(PropertyType* p) : property_(p) {}

    operator typename PropertyType::GetterReturnType() {
        // Log some interesting stuff
        return property_->Get();
    }

    LOGGING_WRAPPER_PROXY& operator=(typename PropertyType::GetterReturnType val) {
        // Log some interesting stuff
        property_->Set(val);
        return *this;
    }

    PropertyType* property_;
};

template<typename Retriever, typename Updater, typename OwningClass, template<typename PropertyType> class WRAPPER_PROXY = DEFAULT_WRAPPER_PROXY>
struct Wrapper {
    Wrapper(Retriever retriever, Updater updater, OwningClass* owner) : retriever_(retriever), updater_(updater), containingClass_(owner) {}

    using GetterReturnType = std::invoke_result_t<Retriever, OwningClass>;
    GetterReturnType Get() { return (containingClass_->*retriever_)(); }

    template<typename...Args>
    using SetterReturnType = std::invoke_result_t<Updater, OwningClass, Args...>;

    template<typename...Args>
    SetterReturnType<Args...> Set(Args&&... args) { return (containingClass_->*updater_)((forward<Args>(args))...); }

    WRAPPER_PROXY<Wrapper<Retriever, Updater, OwningClass>> operator*() {
        return  WRAPPER_PROXY(this);
    }

    Retriever retriever_;
    Updater updater_;
    OwningClass* containingClass_;
};

template<
    template<typename PropertyType>
class WRAPPER_PROXY,
    typename Retriever,
    typename Updater,
    typename OwningClass>
auto MakeWrapper(Retriever&& retriever, Updater&& updater, OwningClass* const owner) {
    return Wrapper<Retriever, Updater, OwningClass, WRAPPER_PROXY>(
        std::forward<Retriever>(retriever),
        std::forward<Updater>(updater),
        owner
        );
}

int main() {

    testclass tc;

    // Cleaner (using CTAD here, could also use auto)
    Wrapper pp3 = MakeWrapper<LOGGING_WRAPPER_PROXY>(
        &testclass::get,
        &testclass::set,
        &tc
        );

    *pp3 = 100;

    Wrapper pp4 = MakeWrapper<DEFAULT_WRAPPER_PROXY>(
        &testclass::get,
        &testclass::set,
        &tc
        );

    *pp4 = 100;
}

This does not compile on *pp3 = 100, because VS2022 states

cannot convert from 'LOGGING_WRAPPER_PROXY<Wrapper<double (__cdecl testclass::* )(void),bool (__cdecl testclass::* )(double),testclass,LOGGING_WRAPPER_PROXY>>' 

to 'LOGGING_WRAPPER_PROXY<Wrapper<Retriever,Updater,OwningClass,DEFAULT_WRAPPER_PROXY>>'


with
[
Retriever=double (__cdecl testclass::* )(void),
Updater=bool (__cdecl testclass::* )(double),
OwningClass=testclass
]

I don't understand why it is trying to create a DEFAULT_WRAPPER_PROXY, what is going wrong here please?

CodePudding user response:

The cause of the problem:
struct Wrapper is a struct with 4 template parameters, and the last of them has a default:

template<typename Retriever, 
         typename Updater, 
         typename OwningClass, 
         template<typename PropertyType> class WRAPPER_PROXY = DEFAULT_WRAPPER_PROXY>
struct Wrapper { /* ... */ }

In this line in struct Wrapper:

WRAPPER_PROXY<Wrapper<Retriever, Updater, OwningClass>> operator*() {

You don't specify the last template parameter of Wrapper, and therefore the default of DEFAULT_WRAPPER_PROXY is used.
This will cause a compilation error when trying to use operator* with the result of MakeWrapper<LOGGING_WRAPPER_PROXY>.

The solution:
You can fix it as shown below by adding the last template argument explicitly:

WRAPPER_PROXY<Wrapper<Retriever, Updater, OwningClass, WRAPPER_PROXY>> operator*() {
  • Related