Trong this presentation vào khoảng 00:19:00, Andrei Alexandrescu giải thích việc triển khai macro SCOPE_EXIT
của mình. Ông tạo ra một đối tượng ScopeGuard
trên stack mà thực hiện một lambda tiêu hủy:Làm cách nào để __COUNTER__ có thể gây ra vi phạm ODR ở đây?
#define ANONYMOUS_VARIABLE(str) \
CONCATENATE(str, __COUNTER__)
namespace detail {
enum class ScopeGuardOnExit {};
template <typename Fun>
ScopeGuard<Fun>
operator+(ScopeGuardOnExit, Fun&& fn) {
return ScopeGuard<Fun>(std::forward<Fun>(fn));
}
}
#define SCOPE_EXIT \
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
= ::detail::ScopeGuardOnExit() + [&]()
Cho đến nay, quá nổi tiếng (ông thậm chí khẳng định trong slide của mình rằng đây là một chiếc mũ cũ). Việc sử dụng trông như thế này:
void foo()
{
SCOPE_EXIT{ printf("foo exits"); };
}
Nhưng tại 01:04:00, Chandler Carruth tuyên bố rằng việc sử dụng này của __COUNTER__
vĩ mô để tạo ra một cái tên "vô danh" sẽ gây ra vi phạm ODR khi được sử dụng trong một hàm inline. Điều này có đúng không? Macro chỉ được sử dụng để tạo tên biến cục bộ, không phải tên loại hoặc thứ gì đó, vậy làm thế nào điều này có thể gây ra vi phạm ODR?
Tôi chỉ đến cùng một kết luận, đã cho tôi một thời gian để nhận ra rằng đó là về định nghĩa của hàm riêng của mình. Nhưng điều này có ý nghĩa, ngay cả khi nó chỉ là về một tên biến khác. Tôi đoán người ta chỉ có thể hy vọng rằng điều này không gây ra bất kỳ vấn đề ... có lẽ các macro LINE sẽ là một lựa chọn tốt hơn ở đây? – Horstling
Có, trừ khi bạn muốn khai báo nhiều hơn một trong những thứ đó trên một dòng. (Hoặc bạn đang rối tung với '# line'.) –