Переадресация методов C++
Мне нужно реализовать класс Container, который действует точно так же, как содержащийся класс шаблона:
template <typename T>
class Container {
public:
//...
private:
// T data_;
};
T
может быть предопределенным типом (например, int
) или пользовательский тип.
Цель состоит в том, чтобы перехватывать любые операции чтения / записи, выполняемые с содержащимся типом.
Я успешно реализовал большинство операторов, и это работает.
Однако, когда мне нужно получить доступ к методам, специфичным для содержащегося класса T, это не сработает:
Container<myclass> a;
a.myclass_specific_method();
Причина в том, что Контейнер явно не имеет таких методов. Более того, поскольку T является шаблоном, его методы не могут быть известны заранее.
Я думаю, что нет решения этой проблемы, даже с C++11, потому что operator .
не может быть перегружен. Поэтому единственный возможный подход - это всегда полагаться на operator->
как умные указатели делают.
Можешь подтвердить?
3 ответа
Для типа класса T
, это будет действовать как T
:
template<class T, class=void>
struct Container : public T { // inheritance MUST be public
using T::T;
Container() = default; // or override
Container( Container const& ) = default; // or override
Container( Container && ) = default; // or override
Container& operator=( Container const& ) = default; // or override
Container& operator=( Container && ) = default; // or override
// here, we override any method we want to intercept
// these are used by operators:
friend T& get_t(Container& self){return self;}
friend T const& get_t(Container const& self){return self;}
friend T&& get_t(Container&& self){return std::move(self);}
friend T const&& get_t(Container const&& self){return std::move(self);}
};
для некласса T
мы обнаруживаем это и используем другую реализацию:
template<class T>
struct Container<T, typename std::enable_if<!std::is_class<T>{}>::type > {
T t;
Container() = default; // or override
Container( Container const& ) = default; // or override
Container( Container && ) = default; // or override
Container& operator=( Container const& ) = default; // or override
Container& operator=( Container && ) = default; // or override
// these are used by operators:
friend T& get_t(Container& self){return self.t;}
friend T const& get_t(Container const& self){return self.t;}
friend T&& get_t(Container&& self){return std::move(self).t;}
friend T const&& get_t(Container const&& self){return std::move(self).t;}
};
наконец, мы отключаемся и перезаписываем каждый оператор, который можем найти в SFINAE, где оператор участвует только в разрешении перегрузки, если get_t(Container)
будет работать на своем месте в операторе. Все это должно быть сделано в пространстве имен, чтобы операторы находились через ADL. Перегрузка get_t
который возвращает свой аргумент без изменений, может быть полезен для массового уменьшения количества перегрузок.
Это может быть еще 100 или более строк кода.
Пользователи Container<T>
может обойти Container<T>
и получить базовый T
в вышеуказанной системе.
Комитет C++ в настоящее время изучает "перегруженный operator .
"для будущих пересмотров языка.
Однако в вашем конкретном случае вы можете просто наследовать от типа.
template <typename T>
class Container : private T {
public:
using T::something_publicly_accessible;
};
Вы против того, чтобы иметь добытчик для внутреннего data
член? Если нет, то вы можете использовать что-то вроде этого
#include <iostream>
#include <string>
template <typename T>
class Container
{
public:
Container(T _data) : data{_data} {}
T GetData() const { return data; }
private:
T data;
};
int main()
{
Container<std::string> c{"foo"};
std::cout << c.GetData().size();
}
В противном случае вы можете получить внутренний доступ к методу, и он будет компилироваться, только если такой метод существует для T
#include <iostream>
#include <string>
template <typename T>
class Container
{
public:
Container(T _data) : data{_data} {}
std::size_t size() const { return data.size(); }
private:
T data;
};
int main()
{
Container<std::string> c{"foo"};
std::cout << c.size();
}
Так что этот последний метод будет работать, если T
был, например, std::string
, std::vector
, std::list
, так далее.