Можно ли привязать метод класса (в shared_ptr) к статической функции в классе признаков? - PullRequest
0 голосов
/ 29 января 2019

Исторически я использовал классы черт, чтобы хранить информацию и применять ее в «универсальной» функции, которая выполняет тот же «алгоритм».Отличались только чертой класса.Например: https://onlinegdb.com/ryUo7WRmN

enum selector { SELECTOR1, SELECTOR2, SELECTOR3, };

// declaration
template < selector T> struct example_trait;

template<> struct example_trait<SELECTOR1> {  
static constexpr size_t member_var = 3;  
static size_t do_something() { return 0; }
};

template<> struct example_trait<SELECTOR2> {  
static constexpr size_t member_var = 5; 
static size_t do_something() { return 0; }  
};


// pretend this is doing something useful but common
template < selector T, typename TT = example_trait<T> > 
void function() { 
std::cout << TT::member_var << std::endl; 
std::cout << TT::do_something() << std::endl;
}

int main()
{
    function<SELECTOR1>();
    function<SELECTOR2>();
    return 0;
}

Я не уверен, как создать «универсальные» алгоритмы, это при работе с полиморфными классами.

Например: https://onlinegdb.com/S1hFLGC7V

Ниже я создал унаследованную иерархию классов.В этом примере у меня есть базовый пример catch-all, который по умолчанию использует все параметры (0 в данном случае).И затем каждый производный набор классов переопределяет определенные методы.

#include <iostream>
#include <memory>
#include <type_traits>
#include <assert.h>    

using namespace std;

struct Base {
 virtual int get_thing_one() {
     return 0;
    }

 virtual int get_thing_two() {
     return 0;
    }

 virtual int get_thing_three() {
     return 0;
    }

 virtual int get_thing_four() {
     return 0;
    }    
};

struct A : public Base {
    virtual int get_thing_one() override {
     return 1;
    }

  virtual int get_thing_three() override {
     return 3;
 }  
};

struct B : public Base {
    virtual int get_thing_one() override {
     return 2;
    }    

    virtual int get_thing_four() override{
     return 4;
 }    
};

Здесь я создал простую фабрику, не элегантную, а для иллюстративных целей

// example simple factory
std::shared_ptr<Base> get_class(const int input) {
    switch(input)
    {
        case 0:
            return std::shared_ptr<Base>(std::make_shared<A>());
        break;

        case 1:
            return std::shared_ptr<Base>(std::make_shared<B>());
        break;

        default:
            assert(false);
        break;
    }
}

Так что это интересующий класс.Это класс «что-то» делает с данными из классов выше.Приведенные ниже методы представляют собой простой пример сложения, но представьте себе более сложный алгоритм, который очень похож для каждого метода.

// class that uses the shared_ptr
class setter {
    private:

    std::shared_ptr<Base> l_ptr;

    public:

    setter(const std::shared_ptr<Base>& input):l_ptr(input)
    {}

    int get_thing_a()
    {
        return l_ptr->get_thing_one() +  l_ptr->get_thing_two();
    }

    int get_thing_b()
    {
        return l_ptr->get_thing_three() +  l_ptr->get_thing_four();
    }
};

int main()
{
    constexpr int select = 0;
    std::shared_ptr<Base> example = get_class(select);
    setter l_setter(example);

    std::cout << l_setter.get_thing_a() << std::endl;
    std::cout << l_setter.get_thing_b() << std::endl;

    return 0;
}

Как я могу сделать «шаблон» внутри класса сеттера более универсальным?Я не могу использовать черты, как в примере выше, потому что я не могу связать статические функции с объектом.Так есть ли способ сделать шаблонный пример более распространенным?

Где-то по линии наличия селектора, скажем

enum thing_select { THINGA, THINGB, };

template < thing_select T >
struct thing_traits;

template <>
struct thing_traits<THINGA>
{
     static int first_function() --> somehow tied to shared_ptr<Base> 'thing_one' method
     static int second_function() --> somehow tied to shared_ptr<Base> 'thing_two' method
}

template <>
struct thing_traits<THINGB>
{
     static int first_function() --> somehow tied to shared_ptr<Base> 'thing_three' method
     static int second_function() --> somehow tied to shared_ptr<Base> 'thing_four' method
}

// generic function I'd like to create
template < thing_select T, typename TT = thing_traits<T> >
int perform_action(...)
{
   return TT::first_function(..) + TT::second_function(..);
}

В идеале я хотел бы изменить приведенный выше класс на что-тостроки

    // Inside setter class further above
    int get_thing_a()
    {
        return perform_action<THINGA>(...);
    }

    int get_thing_b()
    {
        return perform_action<THINGB>(...);
    }

Ответ, возможно, я не могу, и мне нужно передать int shared_ptr в качестве параметра и вызвать конкретные методы, которые мне нужны, вместо того, чтобы пытаться привязать метод shared_ptr кстатическая функция (задним числом это не звучит как хорошая идея ... но я хотел отказаться от своей идеи)

1 Ответ

0 голосов
/ 30 января 2019

Тот, кто совершает реальный вызов, будет нуждаться в ссылке на объект, так или иначе.Поэтому, если вы хотите, чтобы perform_action выполнил фактический вызов, вам нужно будет передать параметр.

Теперь, если вы действительно хотите сохранить, какую функцию из Base вызывать как static вthing_traits не передавая параметр, вы можете использовать указатель на функции-члены:

template <>
struct thing_traits<THINGA>
{
    static constexpr int (Base::*first_function)() = &Base::get_thing_one;
    ...
}

template < thing_select T,  typename TT = thing_traits<T>>
int perform_action(Base & b)
{
   return (b.*TT::first_function)() + ...;
}

Вместо этого вы можете играть, возвращая объект функции, который вызывает вас (а внутренняя функция принимает параметр).

Все зависит от того, кому вам нужно позвонить, и какую информацию / зависимости вы считаете доступными в каждом классе / шаблоне.

...