C ++ Method Forwarding

I need to implement a Container class that acts exactly the same as the contained template class:

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

      

T

can be either a predefined type (for example int

) or a user-defined type.

The goal is to intercept any read / write operations performed on the contained type.

I have followed most of the statements successfully and it works.

However, when I need to access the methods related to the contained class T, it doesn't work:

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

      

The reason is that the Container obviously doesn't have such methods. Moreover, since T is a template, its methods cannot be known in advance.

I think there is no solution to this problem, even with C ++ 11, because it operator .

cannot be overloaded. So the only possible approach is to always rely on operator->

, as smart pointers do.

Can you confirm?

+3


source to share


3 answers


For a class type, T

this will act like a 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);}
};

      

for a non-class T

, we detect it and use a different implementation:

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;}
};

      



finally, we go away and redefine every operator we can find in the friendly SFINAE format, where the operator is only involved in overload resolution if get_t(Container)

it works in its place in the operator. All of this has to be done in a namespace, so the operators are discovered through ADL. An overload get_t

that returns its argument unchanged can be useful to massively reduce the number of overloads.

It could be another 100 or more lines of code.

Users Container<T>

can bypass Container<T>

and get basic T

in the above system.

+2


source


The C ++ committee is currently reviewing "overloads operator .

" for future language changes.

However, in your specific case, you can simply inherit the type.



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

      

+13


source


Are you opposed to having an internal member getter data

? If not, then you can use something like this

#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();
}

      

Otherwise, you can access this method internally and it will only compile if such a method exists for 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();
}

      

So this last method would work if there T

was, for example. std::string

, std::vector

, std::list

Etc.

+1


source







All Articles