Вопрос по class, scope, pointers, templates, c++ – Как получить экземпляр шаблона класса из оператора if? (C ++)

5

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

template <class T> class X{ 
public:
    int A;
    int B;
    T** pData;
    X(int a,int b);
    ~X();        
    void print(); //function which prints pData to screen

};  
template<class T>X<T>::X(int a, int b){ //constructor
    A = a;
    B = b;
    pData = new T*[A];
    for(int i=0;i<A;i++)
        pData[i]= new T[B];
    //Fill pData with something of type T
}
int main(){
    //...
    std::cout<<"Give the primitive type of the array"<<std::endl;
    std::cin>>type;
    if(type=="int"){
        X<int> XArray(a,b);
    } else if(type=="char"){
        X<char> Xarray(a,b);
    } else {
        std::cout<<"Not a valid primitive type!";
    } // can be many more if statements.
    Xarray.print() //this doesn't work, as Xarray is out of scope.
}

Поскольку экземпляр Xarray создается внутри оператора if, я не могу использовать его где-либо еще. Я пытался создать указатель перед операторами if, но так как тип указателя неизвестен, у меня ничего не получилось.

Что было бы правильным способом решения этой проблемы?

Вот,это может помочь тебе bash.d
На это нет «очевидного» ответа, потому что C ++ является статически типизированным. Вы не можете запросить у пользователя тип, а затем создать этот тип и использовать его в другом месте - вы должны знать тип во время компиляции! Одним из методов, который обычно решает эту ситуацию, является «стирание типа», но для этого требуется, чтобывы укажите некоторый общий элемент, который должен быть у всех ваших типов, и взаимодействуйте только через этот общий элемент (например, «для печати»). Kerrek SB

Ваш Ответ

4   ответа
1

Вместо того, чтобы пытаться вписать шаблоны вmain Я бы пошел противоположным путем, чем остальные предложения ... переместить кодвне изmain и в свою собственную (возможно, шаблонную) функцию, которая должна иметь дело с одним типом:

template <typename T>
void generateAndPrint(int a, int b) {
   X<T> x(a,b);
   x.print();
}
int main() { ...
   if (type=="int") generateAndPrint<int>(a,b);
   else if (type=="char") generateAndPrint<char>(a,b);
   else ...
}
1

это означает, что вы должны знать тип объектов во время компиляции. В этом случае вы основываете тип объекта, созданного на основе пользовательского ввода, поэтому невозможно узнать тип во время выполнения.

Наиболее распространенным способом решения этой проблемы является использованиединамический полиморфизм в котором функции вызываются через общий интерфейс, используяпозднее связывание, Мы достигаем этого в C ++, используявиртуальные функции, Например:

struct IPrintable {
   virtual void print() = 0;
};

template<class T>
class X : public IPrintable {
  // Same code as you showed above.
};

int main() {
  std::cout<<"Give the primitive type of the array"<<std::endl;
  std::cin>>type;

  std::unique_ptr<IPrintable> XArray;

  if(type=="int"){
      XArray.reset(new X<int>(a,b));
  } else if(type=="char"){
      XArray.reset(new X<char>(a,b));
  } else {
      std::cout<<"Not a valid primitive type!";
  } // can be many more if statements.

  Xarray->print() // this works now!
}

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

Спасибо, все ответы были очень полезны. Я выбрал это как принятое, так как это именно то, о чем я думал, но не знал, если и как это можно сделать. Jouni Helske
Я не хотел подразумевать, что это единственный вариант. Но обычно это самый простой вариант, когда другие факторы не имеют значения. Учитывая точный вопрос ОП, нет причин использовать что-то более сложное, например, обратные вызовы или посетителей. Кроме того, вопрос о копировании спорного при условии, базовые классов всех пустых интерфейсов, так как вам не придется беспокоиться о нарезании. Chris Hayden
Вы не можете получить прямой доступ к pData, используя эту технику. Если вам нужно что-то сделать с pData, то есть два основных варианта. Во-первых, это расширение для печати и / или определение другого интерфейса, чтобы XArray реализовал его так, чтобы он делал то, что вам нужно, с pData. Второе - использовать шаблон посетителя, как предлагается в другом решении. Chris Hayden
Наследование не единственно возможное решение. В других языках у вас может не быть выбора, но в C ++ есть много способов достижения этого результата. Создание базового класса и обеспечение наследования только потому, что два объекта имеют общий атрибут, кажется мне немного экстремальным. Не говоря уже о том, что это каким-то образом заставляет типы становиться «классами сущностей» и не позволяет им быть «классами значений» (их нельзя легко скопировать, и сравнение становится более сложным). ereOn
Обратите внимание, что я не сказал, что это было плохое решение. Мы просто не можем знать (ОП, возможно, не говорит нам обо всех ограничениях, с которыми ему приходится сталкиваться). Посетителине сложный. На самом деле это одна из самых глупых вещей, которые я когда-либо видел. Просто я не думаю, что люди, изучающие C ++, не должны поощряться думать, что объектно-ориентированное программирование с наследованием является единственным решением: я вижу каждую неделю десятки Java-программистов, которые думают об этом, и это действительно скучно. , ereOn
4

X<int> а такжеx<char> совершенно не связанные типы.

Тот факт, что они оба являются результатом одного и того же шаблонного класса, здесь не поможет.

Я вижу несколько решений, но это зависит от того, что вам действительно нужно.

Вы могли бы, например, сделатьX<> экземпляры происходят от общего не шаблонного базового класса, который имеетprint() метод (в конце концов, как чисто виртуальный). Но прежде чем сделать это, убедитесь, что это имеет смысл на функциональном уровне: следует использовать наследование, потому что оно имеет смысл, а не только из-за технических ограничений. И если вы сделаете это, вы, вероятно, захотите иметь виртуального деструктора.

Вы также можете связать и хранитьstd::function<void ()> к методу, который вы хотите вызвать, но убедитесь, что объекты все еще "живы" (их нет в вашем текущем коде: обаX<int> а такжеX<char> уничтожены, когда они выходят за рамки, прежде чем вы на самом деле позвонитеprint()).

Окончательное решение будет заключаться в создании некоторого варианта типа, который совместим с обоимиX<int> а такжеX<char> (повышение :: вариант <> может помочь здесь). Затем вы можете написать посетителя, который реализуетprint() функциональность для каждого типа.

Выбрав последнее решение, оно станет примерно таким:

typedef boost::variant<X<int>, X<char>> genericX;

class print_visitor : public boost::static_visitor<void>
{
public:
    template <typename SomeType>
    void operator()(const SomeType& x) const
    {
        // Your print implementation
        // x is your underlying instance, either X<char> or X<int>.
        // You may also make several non-templated overloads of
        // this operator if you want to provide different implementations.
    }
};

int main()
{
  boost::optional<genericX> my_x;

  if (type=="int") {
    my_x = X<int>(a,b);
  } else if(type=="char") {
    my_x = X<char>(a,b);
  }

  // This calls the appropriate print.
  if (my_x) {
    boost::apply_visitor(print_visitor(), *my_x)
  }
}

На самом деле нам не хватает знаний, чтобы дать однозначный ответ: если ваши классы являются «сущностями», то вам, вероятно, следует перейти к наследованию. Если они больше похожи на «классы значений», то вариантный путь может быть более подходящим.

1

независимо от их типа, сами шаблоны не помогут. В настоящее время нет абсолютно никакой связи междуX<int> а такжеX<char>.

Если вы хотите рассматривать их как два подтипа общего типа, вам придется использовать наследование (и динамически размещаемые переменные). Например, всеX<T> может наследовать один и тот же базовый класс, скажемPrintableи вы можете хранить данные вunique_ptr<Printable>:

unique_ptr<Printable> r;
if(type=="int"){
    r.reset(new X<int>(a,b));
} else if(type=="char"){        
    r.reset(new X<char>(a,b);
}
r->print();

Но это, наверное, не самый лучший дизайн.

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

template<class T>
void workWithType(int a, int b)
{
   X<T> Xarray(a, b);
   Xarray.print();
}

//...

if(type=="int"){
    workWithType<int>(a,b);
} else if(type=="char"){
    workWithType<char>(a,b);
} 
Наследование не обязательно означает, что ему придется использовать динамическое распределение. ereOn

Похожие вопросы