3

мне нужно реализовать контейнер класса, который действует так же, как содержащийся в нем классе шаблона:метода C++, пересылка

template <typename T> 
class Container { 
public: 
     //... 
private: 
     // T data_; 
}; 

T может быть либо предопределенный типа (например, int) или определяемого пользователем типа.

Целью является перехват любых операций чтения/записи, выполняемых в указанном типе.

Я успешно реализовал большинство операторов, и он работает.

Однако, когда мне нужно получить доступ к методам конкретных содержавшихся класса T, он не работает:

Container<myclass> a; 
a.myclass_specific_method(); 

Причина заключается в том, что Контейнер явно не имеет таких методов. Более того, поскольку T является шаблоном, его методы не могут быть известны заранее.

Я думаю, что решения этой проблемы нет, даже с C++ 11, потому что operator . не может быть перегружен. Поэтому единственный возможный подход - всегда полагаться на operator->, как и на умные указатели.

Можете ли вы подтвердить?

+0

Не можете ли вы предоставить аксессуар (getter) для 'data_' и использовать его как:' a.get(). Myclass_specific_method(); '? –

+0

Нет, не могу. Я хочу, чтобы Контейнер был максимально прозрачным для программиста. – Claudio

ответ

2

Для типа класса 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 в вышеуказанной системе.

+0

Спасибо. Однако, помимо 'std :: enable_if_t', который, вероятно, должен быть' std :: enable_if', простой 'Container ' не компилируется, поскольку он жалуется на 'error: base type 'int' не может быть структурой или типом класса '. Вы можете проверить? – Claudio

+0

@claudio 'typename std :: enable_if :: type' заменяет' std :: enable_if_t 'в компиляторе C++ 11. Отсутствие типа/typename делает невозможным выполнение того, что он должен (перенаправить неклассический, например, 'int', на вторую реализацию). – Yakk

+0

еще раз благодарю вас. Итак, почему бы вам не изменить код, чтобы он мог работать на любом компиляторе C++ 11? – Claudio

1

Вы против того, чтобы иметь геттер для внутреннего пользователя 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(); 
} 

Так что этот последний метод будет работать если, например, Tstd::string, std::vector, std::list и т.д.

+0

К сожалению, 'T' может быть даже определенным пользователем классом. Более того, я не могу использовать геттер, потому что API должен быть максимально прозрачным для программиста (насколько это возможно, менее инвазивным). – Claudio

13

комитет C++ в настоящее время изучает «перегружен operator .» для будущих изменений языка.

Однако в вашем конкретном случае вы можете просто наследовать тип.

template <typename T> 
class Container : private T { 
public: 
    using T::something_publicly_accessible; 
}; 
+0

Если методы относятся только к известному набору, тогда можно использовать SFINAE. – OMGtechy

+0

Хорошо знать, что комитет (наконец) рассматривает перегрузку «оператора». Что-то, что я забыл сказать, является целью класса: перехватить любое чтение/write на содержащемся типе. К сожалению, я не могу наследовать этот тип, потому что когда я реализую 'operator = (T другой)' вызов в оператор 'T :: operator = (другой),' fail для предопределенных типов (например, 'int'). – Claudio

+0

Да, перегрузка 'оператора. 'было бы хорошо ... общие ссылки! Можете ли вы указать на какой-то статус? – davidhigh