Trình tạo mẫu rõ ràng - nó được sử dụng khi nào?


95

Sau vài tuần nghỉ ngơi, tôi đang cố gắng mở rộng và mở rộng vốn hiểu biết của mình về các mẫu với cuốn sách Mẫu - Hướng dẫn hoàn chỉnh của David Vandevoorde và Nicolai M. Josuttis, và điều tôi đang cố gắng hiểu tại thời điểm này là việc trình bày rõ ràng các mẫu .

Tôi thực sự không gặp vấn đề với cơ chế như vậy, nhưng tôi không thể tưởng tượng được tình huống mà tôi muốn hoặc muốn sử dụng tính năng này. Nếu ai đó có thể giải thích điều đó cho tôi, tôi sẽ rất biết ơn.

Câu trả lời:


67

Được sao chép trực tiếp từ https://docs.microsoft.com/en-us/cpp/cpp/explicit-instantiation :

Bạn có thể sử dụng tính năng thuyết minh rõ ràng để tạo bản khởi tạo của một lớp hoặc hàm được tạo mẫu mà không thực sự sử dụng nó trong mã của bạn. Bởi vì điều này hữu ích khi bạn đang tạo tệp thư viện (.lib) sử dụng mẫu để phân phối, các định nghĩa mẫu không rõ ràng sẽ không được đưa vào tệp đối tượng (.obj).

(Ví dụ: libstdc ++ chứa phần khởi tạo rõ ràng của std::basic_string<char,char_traits<char>,allocator<char> >(là std::string) vì vậy mỗi khi bạn sử dụng các hàm của std::string, mã hàm tương tự không cần phải được sao chép vào các đối tượng. Trình biên dịch chỉ cần tham chiếu (liên kết) các hàm đó tới libstdc ++.)


7
Đúng vậy, các thư viện MSVC CRT có các khởi tạo rõ ràng cho tất cả các lớp luồng, ngôn ngữ và chuỗi, chuyên biệt cho char và wchar_t. Kết quả .lib là hơn 5 megabyte.
Hans Passant

4
Làm thế nào để trình biên dịch biết rằng mẫu đã được khởi tạo một cách rõ ràng ở nơi khác? Nó sẽ không chỉ tạo ra định nghĩa lớp vì nó có sẵn?

@STing: Nếu mẫu được khởi tạo, sẽ có một mục nhập các hàm đó trong bảng ký hiệu.
kennytm

@Kenny: Ý bạn là nếu nó đã được khởi tạo trong cùng một TU? Tôi cho rằng bất kỳ trình biên dịch nào đủ thông minh để không khởi tạo cùng một chuyên ngành nhiều lần trong cùng một TU. Tôi nghĩ lợi ích của việc khởi tạo rõ ràng (liên quan đến thời gian xây dựng / liên kết) là nếu một chuyên ngành được khởi tạo (rõ ràng) trong một TU, thì nó sẽ không được khởi tạo trong các TU khác mà nó được sử dụng. Không?

4
@Kenny: Tôi biết về tùy chọn GCC để ngăn việc khởi tạo ngầm, nhưng đây không phải là tiêu chuẩn. Theo như tôi biết VC ++ không có tùy chọn như vậy. Thể hiện rõ ràng. luôn được quảng cáo là cải thiện thời gian biên dịch / liên kết (thậm chí bởi Bjarne), nhưng để nó phục vụ mục đích đó, trình biên dịch bằng cách nào đó phải biết để không khởi tạo ngầm các mẫu (ví dụ: thông qua cờ GCC), hoặc không được cung cấp định nghĩa mẫu, chỉ một khai báo. Điều này có đúng âm thanh? Tôi chỉ đang cố gắng hiểu lý do tại sao người ta lại sử dụng thuyết minh rõ ràng (ngoài việc giới hạn các loại cụ thể).

84

Nếu bạn xác định một lớp mẫu mà bạn chỉ muốn làm việc cho một vài kiểu rõ ràng.

Đặt khai báo mẫu vào tệp tiêu đề giống như một lớp bình thường.

Đặt định nghĩa mẫu trong tệp nguồn giống như một lớp bình thường.

Sau đó, ở cuối tệp nguồn, chỉ khởi tạo rõ ràng phiên bản bạn muốn có sẵn.

Ví dụ ngớ ngẩn:

// StringAdapter.h
template<typename T>
class StringAdapter
{
     public:
         StringAdapter(T* data);
         void doAdapterStuff();
     private:
         std::basic_string<T> m_data;
};
typedef StringAdapter<char>    StrAdapter;
typedef StringAdapter<wchar_t> WStrAdapter;

Nguồn:

// StringAdapter.cpp
#include "StringAdapter.h"

template<typename T>
StringAdapter<T>::StringAdapter(T* data)
    :m_data(data)
{}

template<typename T>
void StringAdapter<T>::doAdapterStuff()
{
    /* Manipulate a string */
}

// Explicitly instantiate only the classes you want to be defined.
// In this case I only want the template to work with characters but
// I want to support both char and wchar_t with the same code.
template class StringAdapter<char>;
template class StringAdapter<wchar_t>;

Chủ yếu

#include "StringAdapter.h"

// Note: Main can not see the definition of the template from here (just the declaration)
//       So it relies on the explicit instantiation to make sure it links.
int main()
{
  StrAdapter  x("hi There");
  x.doAdapterStuff();
}

1
Có chính xác không khi nói rằng nếu trình biên dịch có toàn bộ định nghĩa mẫu (bao gồm các định nghĩa hàm) trong một đơn vị dịch nhất định, nó sẽ khởi tạo một chuyên môn hóa của mẫu khi cần thiết (bất kể liệu chuyên môn đó đã được khởi tạo rõ ràng trong một TU khác hay chưa)? Tức là, để đạt được các lợi ích thời gian biên dịch / liên kết của việc khởi tạo rõ ràng, người ta chỉ phải đưa vào khai báo mẫu để trình biên dịch không thể khởi tạo nó?

1
@ user123456: Có lẽ phụ thuộc vào trình biên dịch. Nhưng nhiều khả năng đúng trong hầu hết các tình huống.
Martin York

1
có cách nào để khiến trình biên dịch sử dụng phiên bản khởi tạo rõ ràng này cho các kiểu bạn chỉ định trước, nhưng sau đó nếu bạn cố gắng khởi tạo mẫu với kiểu "lạ / không mong muốn", hãy để nó hoạt động "như bình thường", khởi tạo mẫu khi cần thiết?
David Doria,

2
điều gì sẽ là một kiểm tra / thử nghiệm tốt để đảm bảo rằng các thuyết minh rõ ràng đang thực sự được sử dụng? Tức là nó đang hoạt động, nhưng tôi không hoàn toàn bị thuyết phục rằng nó không chỉ khởi tạo tất cả các mẫu theo yêu cầu.
David Doria,

7
Hầu hết các câu chuyện bình luận ở trên không còn đúng nữa kể từ khi c ++ 11: Một khai báo tức thời rõ ràng (một mẫu bên ngoài) ngăn chặn các khởi tạo ngầm: mã nếu không sẽ gây ra một thuyết minh ngầm phải sử dụng định nghĩa thuyết minh rõ ràng được cung cấp ở một nơi khác trong chương trình (thông thường, trong tập tin khác: điều này có thể được sử dụng để giảm thời gian biên dịch) en.cppreference.com/w/cpp/language/class_template
xaxxon

21

Thuyết minh rõ ràng cho phép giảm thời gian biên dịch và kích thước đối tượng

Đây là những lợi ích chính mà nó có thể cung cấp. Chúng đến từ hai hiệu ứng sau được mô tả chi tiết trong các phần bên dưới:

  • xóa các định nghĩa khỏi tiêu đề để ngăn các công cụ xây dựng xây dựng lại các bao gồm
  • định nghĩa lại đối tượng

Xóa định nghĩa khỏi tiêu đề

Thuyết minh rõ ràng cho phép bạn để lại các định nghĩa trong tệp .cpp.

Khi định nghĩa nằm trên tiêu đề và bạn sửa đổi nó, một hệ thống xây dựng thông minh sẽ biên dịch lại tất cả các tệp bao gồm, có thể là hàng chục tệp, làm cho việc biên dịch chậm đến mức khó chịu.

Việc đặt định nghĩa trong tệp .cpp có nhược điểm là các thư viện bên ngoài không thể sử dụng lại mẫu với các lớp mới của riêng chúng, nhưng "Xóa định nghĩa khỏi các tiêu đề được bao gồm nhưng cũng hiển thị các mẫu một API bên ngoài" bên dưới cho thấy một giải pháp.

Xem ví dụ cụ thể bên dưới.

Lợi ích khi xác định lại đối tượng: hiểu được vấn đề

Nếu bạn chỉ xác định hoàn toàn một mẫu trên tệp tiêu đề, mọi đơn vị biên dịch bao gồm tiêu đề đó sẽ kết thúc việc biên dịch bản sao ngầm của chính nó về mẫu cho mọi cách sử dụng đối số mẫu khác nhau được thực hiện.

Điều này có nghĩa là rất nhiều thời gian biên dịch và sử dụng đĩa vô ích.

Dưới đây là một ví dụ cụ thể, trong đó xác định cả main.cppnotmain.cppngầm định MyTemplate<int>do việc sử dụng nó trong các tệp đó.

main.cpp

#include <iostream>

#include "mytemplate.hpp"
#include "notmain.hpp"

int main() {
    std::cout << notmain() + MyTemplate<int>().f(1) << std::endl;
}

notmain.cpp

#include "mytemplate.hpp"
#include "notmain.hpp"

int notmain() { return MyTemplate<int>().f(1); }

mytemplate.hpp

#ifndef MYTEMPLATE_HPP
#define MYTEMPLATE_HPP

template<class T>
struct MyTemplate {
    T f(T t) { return t + 1; }
};

#endif

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

int notmain();

#endif

GitHub ngược dòng .

Biên dịch và xem các ký hiệu với nm:

g++ -c -Wall -Wextra -std=c++11 -pedantic-errors -o notmain.o notmain.cpp
g++ -c -Wall -Wextra -std=c++11 -pedantic-errors -o main.o main.cpp
g++    -Wall -Wextra -std=c++11 -pedantic-errors -o main.out notmain.o main.o
echo notmain.o
nm -C -S notmain.o | grep MyTemplate
echo main.o
nm -C -S main.o | grep MyTemplate

Đầu ra:

notmain.o
0000000000000000 0000000000000017 W MyTemplate<int>::f(int)
main.o
0000000000000000 0000000000000017 W MyTemplate<int>::f(int)

Từ đó man nm, chúng tôi thấy điều đó Wcó nghĩa là biểu tượng yếu, GCC đã chọn vì đây là một hàm mẫu. Biểu tượng yếu nghĩa là mã được tạo ngầm định MyTemplate<int>đã biên dịch cho được biên dịch trên cả hai tệp.

Lý do nó không hoạt động tại thời điểm liên kết với nhiều định nghĩa là trình liên kết chấp nhận nhiều định nghĩa yếu và chỉ chọn một trong số chúng để đưa vào tệp thực thi cuối cùng.

Các số trong đầu ra có nghĩa là:

  • 0000000000000000: địa chỉ trong phần. Số không này là do các mẫu được tự động đưa vào phần riêng của chúng
  • 0000000000000017: kích thước của mã được tạo cho chúng

Chúng ta có thể thấy điều này rõ ràng hơn một chút với:

objdump -S main.o | c++filt

kết thúc bằng:

Disassembly of section .text._ZN10MyTemplateIiE1fEi:

0000000000000000 <MyTemplate<int>::f(int)>:
   0:   f3 0f 1e fa             endbr64 
   4:   55                      push   %rbp
   5:   48 89 e5                mov    %rsp,%rbp
   8:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
   c:   89 75 f4                mov    %esi,-0xc(%rbp)
   f:   8b 45 f4                mov    -0xc(%rbp),%eax
  12:   83 c0 01                add    $0x1,%eax
  15:   5d                      pop    %rbp
  16:   c3                      retq

_ZN10MyTemplateIiE1fEilà tên đọc sai của MyTemplate<int>::f(int)>c++filtquyết định không unmangle.

Vì vậy, chúng ta thấy rằng một phần riêng biệt được tạo ra cho mỗi lần khởi tạo phương thức đơn lẻ và mỗi phần trong số chúng tất nhiên sẽ chiếm không gian trong các tệp đối tượng.

Giải pháp cho vấn đề xác định lại đối tượng

Vấn đề này có thể tránh được bằng cách sử dụng trình diễn thuyết rõ ràng và:

  • giữ định nghĩa trên hpp và thêm extern templatevào hpp cho các loại sẽ được khởi tạo một cách rõ ràng.

    Như đã giải thích tại: việc sử dụng mẫu extern (C ++ 11) extern template ngăn không cho một mẫu đã xác định hoàn toàn được khởi tạo bởi các đơn vị biên dịch, ngoại trừ việc khởi tạo rõ ràng của chúng tôi. Bằng cách này, chỉ phần khởi tạo rõ ràng của chúng tôi sẽ được xác định trong các đối tượng cuối cùng:

    mytemplate.hpp

    #ifndef MYTEMPLATE_HPP
    #define MYTEMPLATE_HPP
    
    template<class T>
    struct MyTemplate {
        T f(T t) { return t + 1; }
    };
    
    extern template class MyTemplate<int>;
    
    #endif
    

    mytemplate.cpp

    #include "mytemplate.hpp"
    
    // Explicit instantiation required just for int.
    template class MyTemplate<int>;
    

    main.cpp

    #include <iostream>
    
    #include "mytemplate.hpp"
    #include "notmain.hpp"
    
    int main() {
        std::cout << notmain() + MyTemplate<int>().f(1) << std::endl;
    }
    

    notmain.cpp

    #include "mytemplate.hpp"
    #include "notmain.hpp"
    
    int notmain() { return MyTemplate<int>().f(1); }
    

    Nhược điểm:

    • nếu bạn là thư viện chỉ tiêu đề, bạn buộc các dự án bên ngoài thực hiện việc khởi tạo rõ ràng của riêng chúng. Nếu bạn không phải là một thư viện chỉ có tiêu đề, giải pháp này có thể là tốt nhất.
    • nếu loại mẫu được xác định trong dự án của riêng bạn và không phải là loại tích hợp sẵn int, có vẻ như bạn buộc phải thêm bao gồm cho nó trên tiêu đề, một khai báo chuyển tiếp là không đủ: mẫu extern & loại không đầy đủ Điều này làm tăng sự phụ thuộc của tiêu đề một chút.
  • di chuyển định nghĩa trên tệp cpp, chỉ để lại khai báo trên hpp, tức là sửa đổi ví dụ ban đầu thành:

    mytemplate.hpp

    #ifndef MYTEMPLATE_HPP
    #define MYTEMPLATE_HPP
    
    template<class T>
    struct MyTemplate {
        T f(T t);
    };
    
    #endif
    

    mytemplate.cpp

    #include "mytemplate.hpp"
    
    template<class T>
    T MyTemplate<T>::f(T t) { return t + 1; }
    
    // Explicit instantiation.
    template class MyTemplate<int>;
    

    Nhược điểm: các dự án bên ngoài không thể sử dụng mẫu của bạn với các loại riêng của chúng. Ngoài ra, bạn buộc phải khởi tạo rõ ràng tất cả các loại. Nhưng có lẽ đây là một sự ngược lại mà từ đó các lập trình viên sẽ không quên.

  • giữ nguyên định nghĩa trên hpp và thêm extern templatevào mọi bao gồm:

    mytemplate.cpp

    #include "mytemplate.hpp"
    
    // Explicit instantiation.
    template class MyTemplate<int>;
    

    main.cpp

    #include <iostream>
    
    #include "mytemplate.hpp"
    #include "notmain.hpp"
    
    // extern template declaration
    extern template class MyTemplate<int>;
    
    int main() {
        std::cout << notmain() + MyTemplate<int>().f(1) << std::endl;
    }
    

    notmain.cpp

    #include "mytemplate.hpp"
    #include "notmain.hpp"
    
    // extern template declaration
    extern template class MyTemplate<int>;
    
    int notmain() { return MyTemplate<int>().f(1); }
    

    Nhược điểm: tất cả các bao gồm phải thêm externvào tệp CPP của họ, điều mà các lập trình viên có thể sẽ quên làm.

Với một trong hai giải pháp đó, nmbây giờ chứa:

notmain.o
                 U MyTemplate<int>::f(int)
main.o
                 U MyTemplate<int>::f(int)
mytemplate.o
0000000000000000 W MyTemplate<int>::f(int)

vì vậy chúng tôi thấy chỉ mytemplate.ocó một biên dịch MyTemplate<int>như mong muốn, trong khi notmain.omain.okhông bởi vì Ucó nghĩa là không xác định.

Xóa định nghĩa khỏi các tiêu đề được bao gồm nhưng cũng hiển thị các mẫu một API bên ngoài trong thư viện chỉ dành cho tiêu đề

Nếu thư viện của bạn không chỉ có tiêu đề, extern templatephương pháp sẽ hoạt động, vì việc sử dụng các dự án sẽ chỉ liên kết đến tệp đối tượng của bạn, tệp này sẽ chứa đối tượng của bản khởi tạo mẫu rõ ràng.

Tuy nhiên, đối với thư viện chỉ dành cho tiêu đề, nếu bạn muốn cả hai:

  • tăng tốc độ biên dịch dự án của bạn
  • hiển thị tiêu đề như một API thư viện bên ngoài để người khác sử dụng nó

thì bạn có thể thử một trong những cách sau:

    • mytemplate.hpp: định nghĩa mẫu
    • mytemplate_interface.hpp: khai báo mẫu chỉ khớp với các định nghĩa từ mytemplate_interface.hpp, không có định nghĩa
    • mytemplate.cpp: bao gồm mytemplate.hppvà thực hiện các đánh giá rõ ràng
    • main.cppvà ở mọi nơi khác trong cơ sở mã: bao gồm mytemplate_interface.hpp, khôngmytemplate.hpp
    • mytemplate.hpp: định nghĩa mẫu
    • mytemplate_implementation.hpp: bao gồm mytemplate.hppvà thêm externvào mọi lớp sẽ được khởi tạo
    • mytemplate.cpp: bao gồm mytemplate.hppvà thực hiện các đánh giá rõ ràng
    • main.cppvà ở mọi nơi khác trong cơ sở mã: bao gồm mytemplate_implementation.hpp, khôngmytemplate.hpp

Hoặc thậm chí tốt hơn có lẽ cho nhiều tiêu đề: tạo một thư mục intf/ implbên trong includes/thư mục của bạn và sử dụng mytemplate.hpplàm tên luôn luôn.

Cách mytemplate_interface.hpptiếp cận trông như thế này:

mytemplate.hpp

#ifndef MYTEMPLATE_HPP
#define MYTEMPLATE_HPP

#include "mytemplate_interface.hpp"

template<class T>
T MyTemplate<T>::f(T t) { return t + 1; }

#endif

mytemplate_interface.hpp

#ifndef MYTEMPLATE_INTERFACE_HPP
#define MYTEMPLATE_INTERFACE_HPP

template<class T>
struct MyTemplate {
    T f(T t);
};

#endif

mytemplate.cpp

#include "mytemplate.hpp"

// Explicit instantiation.
template class MyTemplate<int>;

main.cpp

#include <iostream>

#include "mytemplate_interface.hpp"

int main() {
    std::cout << MyTemplate<int>().f(1) << std::endl;
}

Biên dịch và chạy:

g++ -c -Wall -Wextra -std=c++11 -pedantic-errors -o mytemplate.o mytemplate.cpp
g++ -c -Wall -Wextra -std=c++11 -pedantic-errors -o main.o main.cpp
g++    -Wall -Wextra -std=c++11 -pedantic-errors -o main.out main.o mytemplate.o

Đầu ra:

2

Đã thử nghiệm trong Ubuntu 18.04.

C ++ 20 mô-đun

https://en.cppreference.com/w/cpp/language/modules

Tôi nghĩ rằng tính năng này sẽ cung cấp thiết lập tốt nhất về sau khi nó có sẵn, nhưng tôi chưa kiểm tra nó vì nó chưa có trên GCC 9.2.1 của tôi.

Bạn vẫn sẽ phải khởi tạo rõ ràng để tăng tốc độ / tiết kiệm đĩa, nhưng ít nhất chúng tôi sẽ có một giải pháp lành mạnh cho "Xóa định nghĩa khỏi các tiêu đề được bao gồm nhưng cũng hiển thị các mẫu API bên ngoài" mà không yêu cầu sao chép mọi thứ khoảng 100 lần.

Việc sử dụng mong đợi (không có chú thích rõ ràng, không chắc chắn cú pháp chính xác sẽ như thế nào, hãy xem: Cách sử dụng mẫu thuyết minh rõ ràng với các mô-đun C ++ 20? ) Là một cái gì đó cùng:

helloworld.cpp

export module helloworld;  // module declaration
import <iostream>;         // import declaration
 
template<class T>
export void hello(T t) {      // export declaration
    std::cout << t << std::end;
}

main.cpp

import helloworld;  // import declaration
 
int main() {
    hello(1);
    hello("world");
}

và sau đó biên dịch được đề cập tại https://quuxplusone.github.io/blog/2019/11/07/modular-hello-world/

clang++ -std=c++2a -c helloworld.cpp -Xclang -emit-module-interface -o helloworld.pcm
clang++ -std=c++2a -c -o helloworld.o helloworld.cpp
clang++ -std=c++2a -fprebuilt-module-path=. -o main.out main.cpp helloworld.o

Vì vậy, từ điều này chúng ta thấy rằng clang có thể trích xuất giao diện mẫu + thực hiện thành phép thuật helloworld.pcm, trong đó phải chứa một số đại diện trung gian LLVM của nguồn: Các mẫu được xử lý như thế nào trong hệ thống mô-đun C ++? mà vẫn cho phép đặc tả mẫu xảy ra.

Cách nhanh chóng phân tích bản dựng của bạn để xem liệu nó có thu được nhiều lợi ích từ việc tạo mẫu hay không

Vì vậy, bạn có một dự án phức tạp và bạn muốn quyết định xem việc khởi tạo mẫu có mang lại lợi nhuận đáng kể mà không thực sự thực hiện cấu trúc lại đầy đủ hay không?

Phân tích bên dưới có thể giúp bạn quyết định hoặc ít nhất chọn các đối tượng hứa hẹn nhất để cấu trúc lại trước khi bạn thử nghiệm, bằng cách mượn một số ý tưởng từ: Tệp đối tượng C ++ của tôi quá lớn

# List all weak symbols with size only, no address.
find . -name '*.o' | xargs -I{} nm -C --size-sort --radix d '{}' |
  grep ' W ' > nm.log

# Sort by symbol size.
sort -k1 -n nm.log -o nm.sort.log

# Get a repetition count.
uniq -c nm.sort.log > nm.uniq.log

# Find the most repeated/largest objects.
sort -k1,2 -n nm.uniq.log -o nm.uniq.sort.log

# Find the objects that would give you the most gain after refactor.
# This gain is calculated as "(n_occurences - 1) * size" which is
# the size you would gain for keeping just a single instance.
# If you are going to refactor anything, you should start with the ones
# at the bottom of this list. 
awk '{gain = ($1 - 1) * $2; print gain, $0}' nm.uniq.sort.log |
  sort -k1 -n > nm.gains.log

# Total gain if you refactored everything.
awk 'START{sum=0}{sum += $1}END{print sum}' nm.gains.log

# Total size. The closer total gain above is to total size, the more
# you would gain from the refactor.
awk 'START{sum=0}{sum += $1}END{print sum}' nm.log

Giấc mơ: một bộ đệm trình biên dịch mẫu

Tôi nghĩ giải pháp cuối cùng sẽ là nếu chúng ta có thể xây dựng với:

g++ --template-cache myfile.o file1.cpp
g++ --template-cache myfile.o file2.cpp

và sau đó myfile.osẽ tự động sử dụng lại các mẫu đã biên dịch trước đó trên các tệp.

Điều này có nghĩa là các lập trình viên phải nỗ lực thêm 0 bên cạnh việc chuyển tùy chọn CLI bổ sung đó vào hệ thống xây dựng của bạn.

Phần thưởng phụ của việc khởi tạo mẫu rõ ràng: trợ giúp IDEs liệt kê các bản khởi tạo mẫu

Tôi nhận thấy rằng một số IDE chẳng hạn như Eclipse không thể giải quyết "danh sách tất cả các khởi tạo mẫu được sử dụng".

Vì vậy, ví dụ: nếu bạn đang ở bên trong một mã được tạo mẫu và bạn muốn tìm các giá trị có thể có của mẫu, bạn sẽ phải tìm từng cách sử dụng của hàm tạo và suy ra từng kiểu có thể có.

Nhưng trên Eclipse 2020-03, tôi có thể dễ dàng liệt kê các mẫu được khởi tạo một cách rõ ràng bằng cách thực hiện tìm kiếm Tìm tất cả các cách sử dụng (Ctrl + Alt + G) trên tên lớp, ví dụ:

template <class T>
struct AnimalTemplate {
    T animal;
    AnimalTemplate(T animal) : animal(animal) {}
    std::string noise() {
        return animal.noise();
    }
};

đến:

template class AnimalTemplate<Dog>;

Đây là bản demo: https://github.com/cirosantilli/ide-test-projects/blob/e1c7c6634f2d5cdeafd2bdc79bcfbb2057cb04c4/cpp/animal_template.hpp#L15

Tuy nhiên, một kỹ thuật du kích khác mà bạn có thể sử dụng bên ngoài IDE sẽ là chạy nm -Ctrên tệp thực thi cuối cùng và ghi tên mẫu:

nm -C main.out | grep AnimalTemplate

trực tiếp chỉ ra thực tế Doglà một trong những cách diễn đạt:

0000000000004dac W AnimalTemplate<Dog>::noise[abi:cxx11]()
0000000000004d82 W AnimalTemplate<Dog>::AnimalTemplate(Dog)
0000000000004d82 W AnimalTemplate<Dog>::AnimalTemplate(Dog)

1

Nó phụ thuộc vào mô hình trình biên dịch - dường như có mô hình Borland và mô hình CFront. Và sau đó nó cũng phụ thuộc vào ý định của bạn - nếu bạn đang viết thư viện, bạn có thể (như đã nói ở trên) khởi tạo rõ ràng các chuyên ngành bạn muốn.

Trang GNU c ++ thảo luận về các mô hình tại đây https://gcc.gnu.org/onlineocs/gcc-4.5.2/gcc/Template-Instantiation.html .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.