2014-12-17 1 views
1

У меня есть метод, который принимает ссылку объекта как const, этот метод ничего не меняет, а константа указывает, что дело в том, что этот метод также вызывает другой метод, который находится внутри класса и является недействительным, не принимает никаких аргументов и также является виртуальным, что означает, что класс, который расширяет базовый класс, может переопределить метод, но он также должен быть const. Например:const-correctness in void methods и лямбда 'трюк'

#include <iostream> 

class Boz 
{ 
public: 
    virtual void introduce() const = 0; 
}; 

class Foo 
{ 
public: 
    virtual void callable() const 
    { 
     // ... 
    } 

    void caller(const Boz& object) const 
    { 
     callable(); 
     object.introduce(); 
    } 
}; 

class Bar : public Boz 
{ 
public: 
    void introduce() const 
    { 
     std::cout << "Hi." << std::endl; 
    } 
}; 

class Biz : public Foo 
{ 
public: 
    void callable() const 
    { 
     std::cout << "I'm being called before the introduce." << std::endl; 
    } 
}; 

int main(void) 
{ 
    Biz biz; 
    biz.caller(Bar()); 

    return 0; 
} 

Выход будет:

I'm being called before the introduce. 
Hi. 

Как вы можете видеть callable сусло быть константной для того, чтобы называться. Если изменить и сделать это:

class Biz : public Foo 
{ 
public: 
    void callable() 
    { 
     std::cout << "I'm being called before the introduce." << std::endl; 
    } 
}; 

Он будет собирать не ошибки выбрасывается, но вызываемый метод не будет называться, но виртуальная, как это определенно как константы. Это совершенно очевидно.

Сложная часть здесь:

class Foo 
{ 
public: 
    virtual void callable() 
    { 
     // ... 
    } 

    void caller(const Boz& object) const 
    { 
     auto trick = [&]() { callable(); }; 

     trick(); 

     object.introduce(); 
    } 
}; 

class Biz : public Foo 
{ 
public: 
    void callable() 
    { 
     std::cout << "I'm being called before the introduce." << std::endl; 
    } 
}; 

Он работает и метод callable называется. Нет ошибок, таких как passing 'const ...' as 'this' argument.

Что я пытаюсь сделать, это позвонить callable без необходимости быть константой, и причина проста: метод ничего не меняет, у него нет доступа к объекту, который начинает передаваться как аргумент на методе caller, то мы предполагаем, что ему не нужно быть const, но компилятор также выдает ошибку. Реальная проблема заключается в том, что callable является виртуальным, и классы могут расширять базовый класс, реализовывать свои собственные callable и пытаться вызвать другие методы, но не могут, если это не const.

Что я хочу, это в значительной степени то, что я должен знать, как я могу вызвать виртуальный метод без необходимости быть константой (причина в том, что я предпочитаю, чтобы пользователи, которые расширили класс и переопределили callable метод только для звонков const методов, и это не то, что я хочу) и, конечно, понять, что происходит с лямбдой и почему это работает.

+0

Ну, вы можете просто передать неконстантный 'Boz &' to 'caller', если вы специально не хотите передавать ему значения ... – wakjah

+0

Объявив аргумент как const, вы обещаете вызывающему, что вы не измените состояние аргумента. Как вы можете уважать это обещание, если затем передать аргумент неизвестному методу, который также не дает того же обещания? –

+3

Лямбда-трюк не похож на что-то, что должно работать, поскольку оно эффективно отбрасывает 'const' на' this'. 'clang' не принимает этот код, но' g ++ 'делает. Вам нужно 'const_cast' сделать что-то вроде этого или, возможно, улучшить идею, перепроектировать ваш код, чтобы вам не нужно было отбрасывать' const'. – zch

ответ

5

Этот код с лямбда определенно не следует обобщать, это просто GCC ошибка (представленный как PR 60463 и PR 60755), который теперь установлен в багажнике СВН, по http://gcc.gnu.org/r210292

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

const_cast<Foo*>(this)->callable(); 

Но это довольно рискованно, по крайней мере по двум причинам

  1. если объект был объявлен const, то это неопределенное поведение, например. const Foo f; f.caller(boz); - это неопределенное поведение.

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

Я хотел бы изменить свой дизайн таким образом, что виртуальная функция, которую вы хотите назвать это const, или функция caller является неконстантной. Все остальное опасно.