Альтернативный дизайн класса для обеспечения правильного поведения, похожего на указатель (указатель на константу)? - PullRequest
1 голос
/ 20 ноября 2011

Я пишу набор параметризованных классов C ++, и мне интересно, что некоторые из них ведут себя аналогично указателям. В частности, я хочу иметь возможность создавать объект с постоянным параметром шаблона из объекта с непостоянным параметром шаблона, но не наоборот. Этот пример кода должен прояснить мои намерения:

int main() {
  myClass<int> mc_int;
  myClass<const int> mc_const_int;

  myClass<const int> mc1(mc_const_int); // This should compile.
  myClass<int> mc2(mc_int);             // This should compile.
  myClass<const int> mc3(mc_int);       // This should compile.
  myClass<int> mc4(mc_const_int);       // This should NOT compile.
}

Мне удалось добиться этого конкретного поведения, создав следующую иерархию классов (упрощенную для удобства чтения):

template <typename T>
class Base {

  // ...

protected:

  template <typename U>
  Base(const Base<U> &obj): _elem(obj._elem) {}

private:

  T _elem;

  friend class Base<const T>;
};

template <typename T>
class myClass: public Base<T> {

  // ...

public:

  template <typename U>
  myClass(const myClass<U> &obj): Base<const U>(obj) {}
};

И это работает, как и ожидалось, но я не совсем удовлетворен этим дизайном, потому что я могу обнаружить только непостоянный параметр шаблона из конструктора, но не из любой другой функции-члена.

Если бы я хотел, например, создать контейнерный класс с методом addAll(), я бы хотел сделать это:

int main() {
  Container<int> c_int;

  c_int.add(new int(1));
  c_int.add(new int(2));
  c_int.add(new int(3));

  Container<const int> c_const_int;

  c_const_int.addAll(c_int); // This should compile.
  c_int.addAll(c_const_int); // This should NOT compile.
}

Но я не знаю, как добиться предыдущего поведения. У кого-нибудь есть идеи альтернативного дизайна для достижения того, что я пытаюсь сделать? Кто-нибудь знает ссылку, где эта проблема обсуждается более подробно?

Заранее спасибо.

Ответы [ 3 ]

1 голос
/ 20 ноября 2011

Один из способов сделать это - частичная специализация шаблона. Вы должны определить класс для не const типов, как обычно:

template <typename T>
struct MyClass {
    void f(T&);
};

Затем определите специализацию

template <typename T>
struct MyClass<T const> {
    void f(T&);
    void f(T const&);
};

К сожалению, это приводит к дублированию кода, но оно должно позволять вам делать то, что вы хотите. Естественно, у вас могут быть функции: MyClass<T>& и MyClass<T const>&.

0 голосов
/ 20 ноября 2011

Одной из возможностей является использование шаблонов функций-членов, позволяющих им получить желаемые комбинации.

#include <utility>

template <class T, class U>
struct const_convertible;

template <class T>
struct const_convertible<T, T>: std::true_type {};

template <class T>
struct const_convertible<T, const T>: std::true_type {};

template <class T>
struct const_convertible<const T, T>: std::false_type {};

template <class T>
class X
{
public:
   X() {}
   X(const X&) {} //copy constructor
   //conversion constructor as needed
   template <class U>
   X(const X<U>&, typename std::enable_if<const_convertible<U, T>::value, void>::type* = 0)
   {}

   template <class U>
   typename std::enable_if<const_convertible<U, T>::value, void>::type f(X<U>) {}
};

int main()
{
   X<int> mut;
   X<const int> con;

   X<int> a(mut);
   //X<int> b(con);
   X<const int> c(mut);
   X<const int> d(con);

   mut.f(mut);
   //mut.f(con);
   con.f(mut);
   con.f(con);

   //X<double> doub;
   //doub.f(mut);
}
0 голосов
/ 20 ноября 2011

Я немного понимаю вопрос.Вы, вероятно, можете использовать специализацию шаблонов.

template <typename T>
class container
{
public:
   template<typename U>
   void addAll(const container<U>& b ){}
private:
   template<>
   void addAll(const container<int>&b); //No implementation
};

container<int> b;
....
container<const int> a;
a.addAll(b) ; //Give a link error, I can't understand why vc2010 compile and run this line though
...