C ++: шаблоны в файлах заголовков уничтожают меня - PullRequest
4 голосов
/ 23 апреля 2011

Я свободно говорю на Java, но очень плохо знаком с C ++. Я определенно не понимаю, что не так - вообще.

Вот код:

// Sort_Search.h
#ifndef SORT_SEARCH_H
#define SORT_SEARCH_H  

using std::vector;

template<typename T> void printVector(vector<T> &list);

#endif



// Sort_Search.cpp

#include <iostream>
#include <vector>

using std::vector;

template<typename T>
void printVector(vector<T> &list) {
    // print every member of the list
    for(int i = 0; i < (int)list.size(); i++) {
        // insert a comma where needed
        if(i != 0)
            cout << ", ";
        cout << list[i];
    }
}

Я продолжаю получать те же ошибки:

sort_search.h (6): ошибка C2182: «printVector»: недопустимое использование типа «void»

sort_search.h (6): ошибка C2998: «int printVector»: не может быть определением шаблона

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

Большое спасибо за любую помощь. Я схожу с ума здесь. ха-ха.

Ответы [ 3 ]

18 голосов
/ 23 апреля 2011

В заголовке необходимо указать пространства имен.

template<typename T> void printVector(std::vector<T> list);
//                                    ^^^^^

Есть несколько вещей, которые необходимо учитывать:

  1. В C ++,параметры (кроме массивов) всегда передаются как тип значения , если вы не укажете его, в отличие от Java, где все объекты передаются как ссылочный тип.Это означает, что если сигнатура функции printVector(std::vector<T> list), список будет скопирован при подаче в printVector.Это часто нежелательно.Поэтому вам нужно изменить его так, чтобы он передавался по ссылке , добавив & к типу:

    template<typename T> void printVector(std::vector<T>& list);
    //                                                  ^
    

    , но сделав его ссылкой, означает изменение list внутри printVector будет распространяться.Вы часто не хотите случайно изменить список.Это может быть выполнено путем установки параметра const ant:

    template<typename T> void printVector(const std::vector<T>& list);
    //                                    ^^^^^
    

    (Делая его константной ссылкой, также имеет то преимущество, что он может принимать значения r).

  2. Также в отличие от Java, в C и C ++ #include не знает, включали ли вы заголовок один раз.#include - это просто механизм копирования и вставки.Это означает, что если каким-то образом компилятор увидит

    #include "Sort_Search.h"
    ...
    #include "Sort_Search.h"
    

    , то будут определены 2 копии printVector, что приведет к ошибке компилятора.Это возможно, если два разных заголовка a.h и b.h включают Sort_Search.h, а некоторые исходные файлы включают в себя a.h и b.h.Чтобы избежать этого, мы всегда должны предоставлять и # include guard , который предотвращает включение файла более одного раза:

    #ifndef SORT_SEARCH_H_m6f2kyhdncxflxr
    #define SORT_SEARCH_H_m6f2kyhdncxflxr
    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    
    template<typename T> void printVector(const std::vector<T>& list);
    
    #endif
    //^^^^
    
  3. A vector<T> не является встроенным типом, поэтому вам нужно #include <vector>, чтобы компилятор знал о существовании такого типа.

    #ifndef SORT_SEARCH_H_m6f2kyhdncxflxr
    #define SORT_SEARCH_H_m6f2kyhdncxflxr
    
    #include <vector>
    //^^^^^^^^^^^^^^^
    
    template<typename T> void printVector(const std::vector<T>& list);
    
    #endif
    
  4. Наконец, template реализован не так, как универсальные шаблоны в Java или C #.Это похоже на механизм копирования и вставки на уровне AST.Каждый раз, когда вы вызываете printVector, компилятор определяет, что такое T (скажем, int), а затем создает новую функцию, заменяя каждый T на int.

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

    #ifndef SORT_SEARCH_H_m6f2kyhdncxflxr
    #define SORT_SEARCH_H_m6f2kyhdncxflxr
    
    #include <vector>
    #include <iostream>
    
    template<typename T> void printVector(const std::vector<T>& list) {
       for (int i = 0; i < list.size(); ++ i) { ... }
    }
    
    #endif
    

    Или, если вы все еще хотите отделить .cpp от .h, вы можете включить.cpp из .h:

    #ifndef SORT_SEARCH_H_m6f2kyhdncxflxr
    #define SORT_SEARCH_H_m6f2kyhdncxflxr
    
    #include <vector>
    
    template<typename T> void printVector(const std::vector<T>& list);
    
    #include "Sort_Search.cpp"
    //^^^^^^^^^^^^^^^^^^^^^^^^
    
    #endif
    
    // Sort_Search.cpp:
    #include <iostream>
    template<typename T> void printVector(const std::vector<T>& list) {
        ...
    }
    
1 голос
/ 23 апреля 2011

Что на самом деле не так с вашим образцом, так это отсутствие директивы #include <vector> в Sort_Search.h. Разумеется, замечания, сделанные другими, также верны.

1 голос
/ 23 апреля 2011

Сделайте вашу жизнь в миллион раз проще и определите все встроенные функции / классы вашего шаблона. Нет смысла иметь отдельный файл .h и .cpp, так как оба они должны быть включены во время компиляции, так что вам не составит никакого труда их разделить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...