выбор во время компиляции указанного c метода, замаскированного под оператор - PullRequest
0 голосов
/ 05 августа 2020

У меня есть такой класс:

struct Base
{
    void aa(int n) const {
        std::cout << "aa() " << field*n << std::endl;
    }

    void bb(int n) const {
        std::cout << "bb() " << field*n*2 << std::endl;
    }

    int field = 2;
};

Я хочу иметь возможность выбирать, во время компиляции , одну из двух реализаций, aa() или bb(), через вызов метода оператора. Что-то вроде:

Base data;
Magic obj(data);
obj.as_AA() * 33; // should call data.aa(33)
obj.as_BB() * 44; // should call data.bb(44)

data не должно дублироваться. И выбор aa() против bb() должен быть решен во время компиляции.

У меня есть решение, которое использует понижающее преобразование, поведение которого теоретически не определено (я думаю). Он строится (с g ++ и clang ++) и работает отлично, но все же ...

struct AA : public Base
{
    void operator*(int n) const {
        return Base::aa(n);
    }
};

struct BB : public Base
{
    void operator*(int n) const {
        return Base::bb(n);
    }
};

struct Chooser
{
    Base obj;

    template<typename WHICH> // will be either AA or BB
    const WHICH& as() const {
        return static_cast<const WHICH&>( obj ); // downcasting
    }
};

В main.cpp:

Chooser ch;
ch.as<AA>() * 5; // prints "aa() 10"
ch.as<BB>() * 7; // prints "bb() 28"

Насколько ненадежно мое решение? (из-за понижения, которое технически не определено)

Видите ли вы альтернативы?

Спасибо

ps: конечно, я мог бы тривиально использовать

Base data;
data.aa(33);
data.bb(44);

, но я действительно хочу получить доступ к различным реализациям через одно и то же имя ie., Оператор *

Я также мог бы использовать шаблонный оператор * в Base и иметь явную специализацию шаблона, однако это вынудило бы меня использовать уродливый синтаксис, который аннулирует назначение оператора:

struct Base {
    \\...
    template<int N> void operator*(int n) const;
};

template<> void Base::operator*<1>(int n) const {
    aa(n);
}

Для чего требуется:

Base data;
data.operator*<1>(44); // ugly

Ответы [ 2 ]

5 голосов
/ 05 августа 2020

Вы можете написать класс Magic следующим образом:

struct Magic  {
   
    Magic(Base &b) : b(b) {}
    
    Base &b;
     
    struct AA {
        Base &b;
        void operator*(int n) const {
        return b.aa(n);
     }
    };
    
    struct BB {
        Base &b;
        void operator*(int n) const {
        return b.bb(n);
     }
    };
    
    AA as_AA() { return AA{b}; }
    
    BB as_BB() { return BB{b}; }
};

Это позволяет избежать наследования, используя вместо этого композицию. Кроме того, отсутствует копия объекта data, поскольку на него делаются только ссылки.

Теперь вы можете использовать именно тот синтаксис вызова, который вам нужен, и он имеет правильное поведение:

Base data;
Magic obj(data);
obj.as_AA() * 33; // calls data.aa(33) -- prints 66
obj.as_BB() * 44; // calls data.bb(44) -- prints 176

Вот демо .

3 голосов
/ 05 августа 2020
• 1000 1004 *

Если вы пытаетесь продолжить go, используя тот же вызывающий код с предварительным выбором, вы можете использовать функцию более высокого порядка:

auto call_aa(Base& base) {
    return [&](int n) { return base.aa(n); };
}

auto call_bb(Base& base) {
    return [&](int n) { return base.bb(n); };
}

...

Base data;
auto aa = call_aa(data);
aa(33);
call_bb(data)(44);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...