предварительное объявление и ошибка функции шаблона - PullRequest
3 голосов
/ 11 августа 2009

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

class TaskScheduler; --> //forward declaration of ‘struct TaskScheduler’
//
//

class TaskEvent {
//
//
};

class HostTask {
//
//
};

template<class T> inline HostTask*
findT(TaskScheduler* tss, T* e)
{
    map<int, HostTask*>::iterator it;
    bool bEq = false;
    for(it = tss->tasks_.begin(); it != tss->tasks_.end(); it++) { --> //error: invalid use of incomplete type ‘struct TaskScheduler’
    if(dynamic_cast<TaskEvent*>(e))
        bEq = dynamic_cast<TaskEvent*>(e)->equal(it->second->ev_);
    else if(dynamic_cast<HostTask*>(e))
        bEq = dynamic_cast<HostTask*>(e)->equal(it->second);
    if(bEq) {
        return it->second;
    }
}
return NULL;
}
//

//class TaskScheduler definition
class TaskScheduler : virtual public HCIEventsHandler {
friend HostTask* findT<TaskEvent>(TaskScheduler* tss, TaskEvent* e); //findT function is used here
//
//
};

Вот сообщение об ошибке, которое я получил, которое также показано в коде: ./bt-taskscheduler.h:159: ошибка: предварительное объявление «struct TaskScheduler» ./bt-taskscheduler.h:229: ошибка: неверное использование неполного типа ‘struct TaskScheduler’

Может кто-нибудь показать мне, что идет не так в этом коде? Любая помощь приветствуется ..

Ответы [ 5 ]

5 голосов
/ 11 августа 2009

В определении findT вы используете tss->tasks_, который разыменовывает указатель на объект типа TaskScheduler, поэтому вам нужно полное определение структуры, а не просто предварительное объявление, видимое в этой точке программы .

Определение struct TaskScheduler должно появиться до определения шаблона функции findT.

1 голос
/ 11 августа 2009

Помимо проблем с предварительным объявлением, похоже, что ваша функция findT действительно должна быть функцией-членом класса планировщика: она широко использует элементы данных планировщика.

Эти члены являются частными, поэтому вам нужен способ опубликовать их и вернуться к декларации friend.

Таким образом, вы либо делаете члены открытыми, либо, что еще лучше, вы реорганизуете функцию findT в функцию-член.

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

//class TaskScheduler definition
class TaskScheduler : virtual public HCIEventsHandler {
 public:
  template<class T> inline HostTask* findT(T* e) const
  {
    map<int, HostTask*>::iterator it;
    bool bEq = false;
    for(it = tasks_.begin(); it != tasks_.end(); it++) { 
       if(dynamic_cast<TaskEvent*>(e))
          bEq = dynamic_cast<TaskEvent*>(e)->equal(it->second->ev_);
      else if(dynamic_cast<HostTask*>(e))
          bEq = dynamic_cast<HostTask*>(e)->equal(it->second);
      if(bEq) {
          return it->second;
      }
    }
    return NULL;
  }


};
1 голос
/ 11 августа 2009

Поскольку вы используете определение TaskScheduler в функциях findT, у вас есть две опции:

  • Переместить определение TaskScheduler над функцией шаблона findT
  • Сделать TaskScheduler вторым шаблоном функции findT

Как это:

template< class U, class T> 
inline HostTask* findT( U* tss, T* e)
{
   //...
}
1 голос
/ 11 августа 2009

Вы используете класс TaskScheduler в заголовке цикла for "tss-> tasks_.begin ()". Компилятор не знает, есть ли у этого класса член "tasks_" или нет.

Это не проблема с вашими шаблонами, любая функция, указанная в заголовочном файле, вызовет такую ​​же ошибку. Прямое объявление класса позволяет только объявить указатели (или ссылки) на этот класс или передать объекты этого класса в качестве параметров. Вы не можете «использовать» класс (вызывать его методы или получать данные члена), пока полностью не определите свой класс.

0 голосов
/ 11 августа 2009

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

Что вас, вероятно, смущает, так это то, что ваш код, вероятно, работает на некоторых компиляторах, даже современных (я знаю, что MSVC неверен в этом отношении, но я не знаю, примет ли он вышеуказанный код *). Эти компиляторы неправильно реализуют так называемый поиск по двухфазному имени .

Двухфазная петля имен - более предсказуемый метод поиска имен, используемый в шаблонах, чем более простая форма, используемая некоторыми компиляторами. В более простой форме определение шаблона анализируется и сохраняется для использования только в том случае, если оно создано, а поиск имен выполняется для всех имен в шаблоне с момента создания экземпляра шаблона. При двухфазном поиске имен имена, используемые в шаблоне, сортируются в зависимые имена и независимые имена . Независимые имена - это имена, которые компилятор может разрешить немедленно - любое имя, которое не зависит от параметра шаблона, прямо или косвенно. Эти имена обрабатываются сразу же после определения шаблона. Зависимые имена, с другой стороны, не могут быть решены немедленно; они сохраняются и затем, когда выполняется создание экземпляра, ищутся в контексте шаблона, но также и в контексте, в котором был создан экземпляр шаблона для аргумент-зависимый поиск только .

Вот пример:

 void foo (int);
 template <typename T> void bar(T t) {
   foo(1.0);
   foo(t);
 }
 void foo (double);

 struct qux {};
 void foo (qux);

 void baz () {
   bar (1.0);
   qux q;
   bar (q);
 }

N.B. Я знаю, что получил метасинтаксические имена в неправильном порядке. Я прошу прощения, но я добавил qux в последний раз и не мог потрудиться переписать свой комментарий.

Каждый раз bar шаблонов вызывается foo дважды. Первый вызов не зависит, поэтому компилятор разрешает его, как только видит. В результате он вызывает foo (int), применяя преобразование, хотя позднее он найдет лучшее определение. Это ничем не отличается от любого другого вызова функции в C ++. Сложный бит приходит со вторым вызовом, который зависит. Первый звонок в baz вызывает bar<double>, последний звонит bar<qux>. Созданный экземпляр bar пытается вызвать foo с объектом типа T. В сценарии double, так как примитивы никогда не используют зависимый от аргумента поиск, результат снова ищется только из bar, и foo(int) найдено. Однако при вызове с qux зависимый от аргумента поиск применяется как в определении , так и в контексте создания экземпляра **, поэтому вызывается foo(qux).

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

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

** Почти в каждом случае контекст создания включает в себя каждое объявление, которое делает контекст определения. Однако есть ключевое слово export, которое может не соответствовать действительности. Это ключевое слово реализовано только в одном интерфейсе компилятора - интересно, почему никто другой не реализовал его? [/ Сарказм]

...