Если честно, мне не нравится виртуальная диспетчеризация, интерфейс класса. По этой причине я хочу реализовать собственные классы без какого-либо базового абстрактного класса. Чтобы быть образом, я реализую MyCustomWidget, и некоторые его методы были реализованы, другие нет, потому что в этом нет необходимости.
// here is my custom widget class, which 'show' method is implemented, but 'close' method is not.
struct MyCustomWidget
{
void show(){ std::cout << "Hey" << std::endl; }
//void close(){ std::cout << "Bye" << std::endl; }
};
// here is your custom widget class, which 'show' is not implemented but 'close' is .
struct YourCustomWidget
{
//void show(){}
void close(){ std::cout << "Bye" << std::endl;}
};
// common widget class, which may stored within any custom widgets.
struct Widget
{
Widget() = default;
template< typename CustomWidget >
void add(CustomWidget cw)
{
auto child = std::make_unique< proxy<CustomWidget> >( std::move( cw ) )
childs.push_back( std::move(child ) );
}
void show()
{
for(auto & e : childs)
e->show();
}
void close()
{
for(auto& e : childs)
e->close();
}
private:
struct proxy_base
{
virtual void show() = 0;
virtual void close() = 0;
virtual ~proxy_base(){}
};
template< typename CustomWidget >
struct proxy : public proxy_base
{
explicit proxy(CustomWidget cw_) : cw( std::move(cw_ ) )
{}
void show() override final
{ // -------------->>>>>> (1)
// call cw.show() if cw has 'show' method, otherwise nothing.
}
void close() override final
{ /// ---------------->>>> (2)
// call cw.close if cw has a 'close' method, otherwise nothing.
}
CustomWidget cw;
};
std::vector< std::unique_ptr< proxy_base > >childs;
};
int main()
{
Widget w;
w.add( MyCustomWidget() );
w.add( YourCustomWidget() );
w.show();
//.... a lot of code
w.close();
}
Мой вопрос прост: как мне реализовать те (1) и (2) виртуальные методы?
Изменить: я вижу, что на этот вопрос уже был дан ответ. Позвольте мне изменить мой вопрос.
Q2: (1) и (2) методы являются «конечными» и в базовом классе они были объявлены как чисто виртуальные, для этой ситуации компилятор может оптимизировать виртуальную таблицу и избежать этого?
Я интересуюсь GCC, CLang и Visual Studio 2013.
Вы можете положить их в private
раздел прокси класса:
template<typename T>
auto show_helper(int) -> decltype( std::declval<T>().show(), void())
{
cw.show();
}
template<typename T>
void show_helper(...) { }
и называть их так от show
:
show_helper<CustomWidget>(0);
Первая перегрузка создается только в том случае, если выражение в конечном типе возврата правильно сформировано, то есть когда T
есть show
метод.
Это называется выражением SFINAE и намного короче, чем версия до C ++ 11 из ответа pmr. Он также более гибкий, так как позволяет указать подпись show
легче. Другой ответ может дать вам положительный результат только для того, чтобы узнать, что вы не можете позвонить show
без аргументов. Выбрать свой яд.
Вы можете получить класс черт SFINAE для проверки, а затем использовать его для отправки на close_impl
, Кроме того, вы также можете использовать класс черт в сочетании с enable_if
выбрать правильную версию close
,
#include <iostream>
#include <type_traits>
template <typename T>
class has_close
{
typedef char one;
typedef long two;
template <typename C> static one test( decltype(&C::close) ) ;
template <typename C> static two test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
struct X { void close() {} };
struct X1 { };
template<typename T>
struct XX {
T t;
void close() {
close_impl(std::integral_constant<bool, has_close<T>::value>{});
}
void close_impl(std::true_type) { std::cout << "call close" << std::endl;t.close(); }
void close_impl(std::false_type) { std::cout << "no close" << std::endl;}
};
int main()
{
XX<X> x; x.close();
XX<X1> x1; x1.close();
return 0;
}