2015-01-23 26 views
8

Tôi muốn có một sự hiểu biết tốt hơn khi trình biên dịch sẽ ngầm nhanh chóng một thành viên chức năng mẫu.C++ Implicit instantiation của các thành viên chức năng mẫu

Hãy xem xét ví dụ sau:

// example.h 

struct Parent { 
    virtual void foo(double) {} 
}; 

struct Child : Parent { 
    void foo(double d) { bar(d); } 

    template<typename T> void bar(T); 

    virtual void baz(); 
}; 


// example.cpp 
#include "example.h" 
template <typename T> 
void Child::bar(T) {} 

void Child::baz() {} 

Biên dịch này với [g++|clang++] -c example.cpp, cả GCC và kêu vang ngầm nhanh chóng các chức năng template Child::bar<double>. Tuy nhiên, những thay đổi dường như nhỏ sau ngăn chặn điều này:

  • Làm foo không ảo
  • Làm Child không kế thừa từ Parent
  • Loại bỏ baz
  • Làm baz không ảo
  • Xác định baz với việc kê khai trong tiêu đề

Có bất kỳ giải thích hợp lý nào ngắn gọn về thời điểm diễn ra sự diễn đạt ngầm định hay không, tôi có cần phải lướt qua một bản sao của tiêu chuẩn không? Tôi đã tìm kiếm SO cho các câu hỏi khác liên quan đến instantiation ngầm, nhưng không tìm thấy nhiều. Tôi tìm thấy gần nhất là this question, nhưng đó là về các hàm ảo trong các mẫu lớp (không phải là các mẫu hàm thành viên).

Để rõ ràng: Tôi hiểu rằng vấn đề này có thể tránh được bằng cách bao gồm định nghĩa trong tiêu đề hoặc mô tả rõ ràng các mẫu mà tôi cần. Điều này chỉ đơn thuần là phát sinh như một điểm tò mò khi đào sâu vào lý do tại sao một lớp (mà tôi đã vô tình bỏ qua những khoảnh khắc rõ ràng) dù được biên soạn và liên kết một cách vui vẻ.

+0

Đơn giản của nó - chúng được khởi tạo chỉ khi được gọi. Tập hợp các trường hợp khác nhau mà bạn có chỉ quyết định khi tham chiếu đó thực sự trở nên cần thiết. Khi bạn có một hàm ảo, nó cần tạo một mục nhập trong bảng ảo. Thanh gọi bên trong foo đang buộc nó o khởi tạo nó. Không có baz, nó cần một vtable cho Child. Làm baz inline không ảnh hưởng đến nó - thats có lẽ kết quả của một tối ưu hóa? Nếu bạn thừa kế một cháu từ con và cho nó một func ảo - BÂY GIỜ nội tuyến không nên ngăn chặn instantiation – excalibur

Trả lời

2

Tôi đã đặt mã của bạn vào Visual Studio 2013 (child.h và child.cpp) và thử như sau:

#include "child.h" 
Child c1; 
c1.bar(10.2); 

này tạo ra một lỗi bên ngoài chưa được giải quyết mà chỉ có "instantiation ngầm" xảy ra. cho thấy có sự khác biệt rõ ràng giữa Visual Studio và G ++ trong trường hợp này. Điều này đã được sửa bằng cách di chuyển mã của Child :: foo vào tệp child.cpp.

Vì vậy, câu trả lời ngắn gọn là: Điều bạn đang trải qua là một trình biên dịch có mức độ lớn cụ thể và cho tính di động bạn không nên dựa vào hành vi này. Theo tiêu chuẩn C++, an toàn nhất là đặt định nghĩa mẫu của bạn vào tệp .h (hoặc .hpp) hoặc khởi tạo nhanh các mẫu theo yêu cầu. Bất kỳ cách nào khác để quản lý điều này sẽ có khả năng phá vỡ trong một số trình biên dịch.

Để hiểu rõ thêm về hành vi của Visual Studio đây hãy xem này:

là gì nói là bất kỳ chức năng được xác định trong định nghĩa của lớp học là hoàn toàn nội tuyến. Trình biên dịch có thể trì hoãn việc khởi tạo hàm nội tuyến nếu nó chọn. Điều này có nghĩa là khi biên soạn trẻ em.obj Child :: foo (d) không bao giờ được tạo ra mà lần lượt có nghĩa là thanh không bao giờ được khởi tạo vì vậy điều này gây ra vấn đề biên dịch trong giai đoạn liên kết. Tại sao Visual Studio thực sự có thể thực hiện điều này vì foo (double) về mặt kỹ thuật là một chức năng ảo thực sự lạ, nhưng có vẻ như Visual Studio rời khỏi instantator của foo() cho sau này khi Child được sử dụng. Vì vậy, ví dụ:

Parent *p1 = new Child(); 

Cũng gây ra một vấn đề là vào thời điểm này trình biên dịch sẽ cố gắng để tạo ra trẻ em :: foo (kép) và có thể không phải vì mẫu định nghĩa mất tích.

Từ kết quả của bạn, giả định là GCC sẽ ngay lập tức khởi tạo các hàm nội tuyến nếu chúng là ảo.

Các hành vi bạn đang gặp là sự kết hợp của sự vật:

  1. Làm thế nào trình biên dịch xử lý các chức năng inline ngầm
  2. Làm thế nào bảng ảo được tạo ra cho các chức năng ảo
  3. Và khi trình biên dịch là cần thiết để khởi tạo các hàm thành viên ảo.

Xem những câu hỏi này để biết thêm thông tin:

Vì vậy:

  • Dường như trình biên dịch được phép trì hoãn việc khởi tạo các hàm nội tuyến cho đến khi được sử dụng và sau đó quyết định xem nó có muốn "nội tuyến" chúng hay tạo một hàm riêng biệt không.
  • Cho đến khi một lớp được sử dụng, trình biên dịch không bắt buộc phải hoàn thành việc khởi tạo mọi thứ cần thiết cho đối tượng
  • Thực tế là tối ưu hóa ngay cả khi tạo đối tượng trình biên dịch có thể không có mọi thứ được xác định trong mã cho lớp .

Tôi đã quyết định không trả lời các truy vấn cụ thể của bạn chi tiết như nghiên cứu của tôi dường như chỉ ra thực tế rằng công việc mã của bạn là hoàn cảnh và hành vi này không phải là điều nên dựa vào.

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