Trước hết: đối tượng là gì?
[intro.object] \ 1
[...] Một đối tượng là một khu vực dung lượng lưu trữ [...]
Việc lưu trữ được phân bổ trước tuổi thọ của một đối tượng bắt đầu:
[basic.life]
Trước khi thời gian của đối tượng đã bắt đầu nhưng sau khi lưu trữ mà đối tượng sẽ chiếm được phân bổ [..] bất kỳ con trỏ nào đề cập đến vị trí lưu trữ nơi đối tượng đó hoặc nằm có thể được sử dụng chỉ theo những cách hạn chế. Đối với một đối tượng đang được xây dựng hoặc phá hủy, xem 12.7 [xây dựng và tiêu hủy]. Nếu không, con trỏ như vậy đề cập đến bộ nhớ được cấp phát (3.7.4.2) và sử dụng con trỏ như thể con trỏ có loại void *, được xác định rõ.
Do đó, con trỏ đề cập đến không gian được phân bổ và không có hại khi sử dụng nó. Bạn chỉ yêu cầu một địa chỉ ngăn xếp và bất kỳ trình biên dịch nào cũng có thể tìm ra chính xác. Không có hoạt động khởi tạo bởi chính đối tượng được yêu cầu trong trường hợp cụ thể này.
này có ý nghĩa vì trong một trình biên dịch AST-thời trang cổ điển nếu bạn để có một cái nhìn tại các hệ thống phân cấp tiêu chuẩn cho declarators, trong một món đồ chơi-mã đơn giản như
class command {
public:
command(int) {
}
};
int funct(command*) {
return 2;
}
int main() {
command com(funct(&com));
}
dòng
command com(funct(&com));
được hiểu như sau:
[dcl.decl]
simple-declaration:
attribute-specifier-seqopt decl-specifier-seqopt init-declarator-listopt;
...
initializer:
brace-or-equal-initializer
(expression-list) // The declaration statement is already specified
Và cuối cùng cho mã của bạn đây là cách gcc biên dịch dòng này (-O0)
command com(std::bind(test_action, &com));
->
movq %rax, -104(%rbp)
leaq -104(%rbp), %rdx
leaq -96(%rbp), %rcx
movl test_action(command*), %esi
movq %rcx, %rdi
movq %rax, -136(%rbp) # 8-byte Spill
movq %rcx, -144(%rbp) # 8-byte Spill
callq _ZSt4bindIRFvP7commandEJS1_EENSt12_Bind_helperIT_JDpT0_EE4typeEOS5_DpOS6_
leaq -80(%rbp), %rax
movq %rax, %rdi
movq -144(%rbp), %rsi # 8-byte Reload
movq %rax, -152(%rbp) # 8-byte Spill
callq _ZNSt8functionIFvvEEC1ISt5_BindIFPFvP7commandES5_EEEET_NSt9enable_ifIXntsr11is_integralISA_EE5valueENS1_8_UselessEE4typeE
movq -136(%rbp), %rdi # 8-byte Reload
movq -152(%rbp), %rsi # 8-byte Reload
callq command::command(std::function<void()> const&)
đó là: chỉ cần một loạt các địa chỉ ngăn xếp từ con trỏ cơ sở đó được truyền cho hàm ràng buộc trước khi gọi hàm tạo.
Mọi thứ sẽ khác nếu bạn thực sự cố gắng sử dụng đối tượng trước khi xây dựng (mọi thứ có thể gặp khó khăn với các bảng chức năng ảo).
Sidenote: đây là NOT đảm bảo an toàn nếu bạn đang sao chép hoặc chuyển giá trị đối tượng và đi ngoài phạm vi (và vẫn giữ địa chỉ cho vị trí ngăn xếp). Ngoài ra: nếu trình biên dịch quyết định để lưu trữ nó (cho bất kỳ kiến trúc/lý do) như là một bù đắp từ khung cơ sở, có thể bạn đang đi đến hành vi không xác định.
Miễn là bạn không thực sự làm bất cứ điều gì với đối tượng trước khi nó được xây dựng, bạn là vàng. Và như @MarcoA đã nhận xét, bạn chỉ sử dụng địa chỉ trước thời điểm đó. – Deduplicator
Có thể là một ý tưởng tốt để sao chép và di chuyển được bảo vệ nếu bạn định thực hiện việc này thường xuyên. –