Home > Back-end >  Tidying function template to avoid duplication: decltype issues
Tidying function template to avoid duplication: decltype issues

Time:07-11

I have the following class which wraps a member function:

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

using namespace std;

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

template<typename Retriever, Retriever retrieverFunc, typename Updater, Updater updaterFunc, typename OwningClass>
struct Wrapper {
    Wrapper(Retriever retriever, Updater updater, OwningClass* owner) : retriever_(retriever), updater_(updater), containingClass_(owner) {}

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

    template<typename...Args>
    using SetterReturnType = std::invoke_result_t<decltype(updaterFunc), OwningClass, Args...>;

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

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


int main() {

    testclass tc;

    Wrapper<decltype(&testclass::get), &testclass::get, decltype(&testclass::set), &testclass::set, testclass> pp(&testclass::get, &testclass::set, &tc);
    pp.Get();
    pp.Set(1.0);

}

Clearly, the syntax for declaring a wrapper is a bit tedious, so I wanted to clean it up a bit. I tried this:

template<typename Retriever, Wrapper Updater, typename OwningClass>
using WrapperSimplified = Wrapper<decltype(Retriever), Retriever, decltype(Updater), Updater, OwningClass>;

with the intention of being able to say this:

WrapperSimplified<&testclass::get, &testclass::set, testclass> pp(&testclass::get, &testclass::set, &tc);

But it does not compile, VS2022 complains

'Retriever': is not a valid type for non-type template parameter 'retrieverFunc'

How can I achieve this please?

CodePudding user response:

Retriever is supposed to be the value, not the type, of the non-type template argument. So it should be declared as non-type template parameter. Since you want the type to be deduced, the type of the non-type template parameter should be auto. Equivalently for Updater:

template<auto Retriever, auto Updater, typename OwningClass>
using WrapperSimplified = Wrapper<decltype(Retriever), Retriever, decltype(Updater), Updater, OwningClass>;

But from what I can tell the class doesn't actually use the non-type template parameters retrieverFunc and updaterFunc. You can remove them (replacing the decltype(retrieverFunc) with Retriever and decltype(updaterFunc) with Updater).

Then the class object can be created simply using CTAD:

Wrapper pp(&testclass::get, &testclass::set, &tc);
  • Related