Переадресация методов 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, так далее.

Другие вопросы по тегам