2009-10-28 47 views
31

Tôi có rất ít ý tưởng về các mẫu C++, nhưng tôi đang cố triển khai hàm tìm kiếm vectơ cho phần tử thỏa mãn thuộc tính đã cho (trong trường hợp này, tìm kiếm cho một cái có tên). Tuyên bố của tôi trong tập tin .h của tôi là như sau:Vấn đề mẫu gây ra lỗi liên kết (C++)

template <typename T> 
T* find_name(std::vector<T*> v, std::string name); 

Khi tôi biên dịch, tôi nhận được lỗi mối liên kết này khi tôi gọi hàm:

Error 1 error LNK2019: unresolved external symbol "class Item * __cdecl find_name<class Item>(class std::vector<class Item *,class std::allocator<class Item *> >,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" ([email protected]@@@@[email protected]@[email protected]@@[email protected]@@@[email protected]@@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@Z) referenced in function "public: class Item * __thiscall Place::get_item(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >)" ([email protected]@@[email protected]@[email protected][email protected]@[email protected]@[email protected]@[email protected]@[email protected]@@Z) place.obj Program2 

Một lần nữa, tôi mới vào mẫu nên tôi không biết chuyện gì đang xảy ra. Tất cả các trường hợp tôi đã tìm thấy của LNK2019 thông qua Google đã được về không sử dụng các thư viện chính xác, nhưng vì đây là chức năng của riêng tôi, tôi không thấy lý do tại sao điều này sẽ xảy ra.

Ngoài ra, một câu hỏi có liên quan: Có cách nào để tạo tham số mẫu để nó phải là lớp con của một lớp nhất định, tức là mẫu không?

+0

bạn đang sử dụng trình biên dịch nào? một số trình biên dịch ngăn bạn phân tách khai báo và định nghĩa thành các tệp riêng biệt cho các mẫu. – Jordan

+0

Bạn có thực sự viết triển khai cho chức năng mẫu của mình không? – begray

+2

Bạn cũng có thể xem xét sử dụng std :: find hoặc std :: find_if –

Trả lời

59

Bạn phải có sẵn các định nghĩa mẫu tại trang web gọi điện. Điều đó có nghĩa là không có các tệp .cpp.

Lý do là không thể biên dịch mẫu. Hãy suy nghĩ về các chức năng như cookie, và trình biên dịch là một lò nướng.

Mẫu chỉ là công cụ cắt cookie, vì chúng không biết chúng là loại cookie nào. Nó chỉ cho trình biên dịch biết làm thế nào để tạo ra hàm khi được đưa ra một kiểu, nhưng trong chính nó, nó không thể được sử dụng vì không có kiểu cụ thể nào được vận hành. Bạn không thể nấu một máy cắt cookie. Chỉ khi bạn có sẵn bột cookie ngon (tức là, với trình biên dịch bột [loại])) bạn có thể cắt cookie và nấu nó không.

Tương tự như vậy, chỉ khi bạn thực sự sử dụng mẫu với một loại nhất định thì trình biên dịch có thể tạo ra hàm thực tế và biên dịch nó. Tuy nhiên, nó không thể làm điều này nếu thiếu định nghĩa mẫu. Bạn phải di chuyển nó vào tệp tiêu đề, vì vậy người gọi hàm có thể tạo cookie.

+0

Cảm ơn bạn. Bây giờ tôi hiểu điều đó, nhưng tôi e rằng tôi không hiểu tại sao nó có thể làm điều này cho các chức năng thông thường nhưng không cho các chức năng mẫu, mà tôi cho rằng tôi có thể viết ra để không hiểu đầy đủ về C++. – marsolk

+0

Mẫu là bản mẫu, giống như macro, tốt hơn (và tệ hơn;). Cho đến khi và trừ khi bạn sử dụng chúng, trình biên dịch không bắt buộc phải thực hiện thay thế giống macro với (các) kiểu đã cho và tạo hàm thực tế. Bây giờ, khi trình biên dịch chạm vào sự khởi tạo này, nó có thể hoặc nó có thể không có bản mẫu trong tay. Nếu nó không bạn đánh một rào cản. – dirkgently

+1

Các mẫu chức năng là * không * chức năng. Các mẫu chức năng là * mẫu *. Họ tuân thủ các quy tắc của riêng họ. – AnT

0

Bạn đã đặt định nghĩa hàm mẫu của mình trong tệp cpp chưa? Sau đó di chuyển nó đến tiêu đề và nội tuyến nó.

+0

Không, điều này là không cần thiết. Xem câu trả lời của Charles Bailey ở trên. –

29

Có thể bạn đang thiếu một phiên bản hợp lệ. Nếu bạn đặt định nghĩa mẫu của bạn trong một tệp .cpp riêng biệt, khi trình biên dịch biên dịch tệp đó, nó có thể không biết bạn cần những phiên bản nào. Ngược lại, tại các trang web gọi sẽ khởi tạo phiên bản đúng của hàm mẫu, nếu định nghĩa của phần thân hàm không có sẵn thì trình biên dịch sẽ không có thông tin để khởi tạo các chuyên môn cần thiết.

Bạn có hai tùy chọn. Đặt thân hàm cho mẫu hàm trong tệp tiêu đề.

ví dụ: trong tệp tiêu đề:

template <typename T> 
inline T* find_name(std::vector<T*> v, std::string name) 
{ 
    // ... 
} 

hoặc nhanh chóng tạo mẫu trong .cpp nơi bạn đã xác định mẫu.

ví dụ: trong file nguồn (có thể sẽ yêu cầu #include ing tập tin định nghĩa Item):

template <typename T> 
T* find_name(std::vector<T*> v, std::string name) 
{ 
    // ... 
} 

template Item* find_name<Item>(std::vector<Item*> v, std::string name); 
+1

Tôi thấy điều này hữu ích hơn so với câu trả lời được chấp nhận vì mẹo tuyệt vời đó về việc buộc phải khởi tạo trong tệp C++. – ancientHacker

9

Những câu trả lời ở đây là rất lớn.

Tôi sẽ chỉ thêm rằng đây thường là lý do tại sao ngoài các tệp .h.cpp trong một dự án. Bạn thường sẽ tìm thấy các tệp .inl. Các định nghĩa mẫu sẽ đi vào tệp .inl.

Các tệp .inl này có nghĩa là nội tuyến và thường sẽ được bao gồm trong tệp .h có cùng tên tiền tố ở cuối tệp sau khi tất cả các khai báo tiêu đề. Điều này có hiệu quả làm cho chúng trở thành một phần của tệp tiêu đề nhưng tách các khai báo khỏi bất kỳ định nghĩa nào.

Vì họ được tôn vinh tập tin tiêu đề, bạn nên thực hiện tất cả các biện pháp phòng ngừa tương tự mà bạn sẽ có một tập tin header thường xuyên, tức là bao gồm bảo vệ, vv

+0

Các tệp '.inl' này có được hỗ trợ bởi chuẩn C++ hay không phụ thuộc vào trình biên dịch? – kim366

0

Tôi chỉ nhận thấy rằng bạn đã có một câu hỏi thứ hai mà có vẻ là chưa được trả lời:

Có cách nào để tạo tham số mẫu để nó phải là lớp con của một lớp nhất định, ví dụ: mẫu không?

Có thể. Ví dụ: xem is_base_of trong Boost.TypeTraits.

Tuy nhiên, tôi tò mò: Tại sao bạn muốn điều đó? Thông thường, các yêu cầu của một mẫu trên các tham số của nó không nằm trong kiểu của tham số, mà trên đó các biểu thức liên quan đến kiểu đó là hợp pháp. Ví dụ, hãy tưởng tượng rằng bạn có:

template<class T> 
void foo(const T& t) 
{ 
    if (t.foo()){ 
     t.bar("blah"); 
    } 
} 

Nói rằng T phải kế thừa từ một cái gì đó như:

class HasFooAndBar 
{ 
public: 
    void foo()const; 
    void bar(const char*)const; 
}; 

mang gì vì instantiation của hàm sẽ thất bại anyway nếu loại không hỗ trợ các hoạt động . Hơn nữa, nó không cần thiết hạn chế khả năng áp dụng của foo(). Trong thực tế, foo của bất kỳ yêu cầu được rằng t.foo()and t.bar(const char*) là biểu thức có giá trị trên một T. const Ví dụ, loại này không kế thừa từ HasFooAndBar và vẫn là một foo hợp lệ() tham số:

struct DifferentFromHasFooAndBar 
{ 
    bool foo()const; 
    std::string bar(const std::string&)const; 
}; 
2

stumbled khi cùng một vấn đề và tìm thấy điều này nêu rõ 3 cách giải quyết: http://www.codeproject.com/Articles/48575/How-to-define-a-template-class-in-a-h-file-and-imp

Trong số đó là một cách dễ dàng, nơi bạn tạo phương thức "giả" trong tệp .cpp, gọi hàm mẫu/lớp với các loại khác nhau. Đã dán từ liên kết:

// No need to call this TemporaryFunction() function, it's just to avoid link error. 
void TemporaryFunction() 
{ 
    TestTemp<int> TempObj; 
    TestTemp<float> TempObj2; 
} 
+2

Tôi thực sự thích phương pháp này, nhưng có cách nào để đảm bảo trình biên dịch sẽ không tối ưu hóa điều này? Tôi đã từng cố gắng tối ưu hóa -O3 và sau đó là 'biểu tượng không xác định' – darwinsenior

Các vấn đề liên quan