Nếu bạn cần loại thứ gì đó không giống như một lệnh gọi hàm, std::result_of
chỉ cần không áp dụng. decltype()
có thể cung cấp cho bạn loại biểu thức bất kỳ.
Nếu chúng ta tự giới hạn mình trong các cách khác nhau để xác định kiểu trả về của một lời gọi hàm (giữa std::result_of_t<F(Args...)>
và decltype(std::declval<F>()(std::declval<Args>()...)
), thì sẽ có sự khác biệt.
std::result_of<F(Args...)
được định nghĩa là:
Nếu biểu thức
INVOKE (declval<Fn>(), declval<ArgTypes>()...)
được hình thành tốt khi được coi như một toán hạng không được đánh giá (Điều 5), thì kiểu typedef thành viên sẽ đặt tên kiểu decltype(INVOKE (declval<Fn>(), declval<ArgTypes>()...));
khác, không có kiểu thành viên.
Sự khác biệt giữa result_of<F(Args..)>::type
và decltype(std::declval<F>()(std::declval<Args>()...)
là tất cả về điều đó INVOKE
. Sử dụng declval
/ decltype
trực tiếp, ngoài việc nhập lâu hơn một chút, chỉ hợp lệ nếu F
có thể gọi trực tiếp (kiểu đối tượng hàm hoặc một hàm hoặc một con trỏ hàm). result_of
bổ sung hỗ trợ con trỏ tới các hàm thành viên và con trỏ tới dữ liệu thành viên.
Ban đầu, việc sử dụng declval
/ decltype
đảm bảo một biểu thức thân thiện với SFINAE, trong khi đó, bạn std::result_of
có thể gặp lỗi khó thay vì lỗi khấu trừ. Điều đó đã được sửa chữa trong C ++ 14: std::result_of
bây giờ được yêu cầu phải thân thiện với SFINAE (nhờ bài báo này ).
Vì vậy, trên một trình biên dịch C ++ 14 phù hợp, std::result_of_t<F(Args...)>
là hoàn toàn vượt trội. Nó rõ ràng hơn, ngắn gọn hơn và chính xác † hỗ trợ nhiều F
s ‡ hơn .
† Trừ khi, bạn đang sử dụng nó trong bối cảnh mà bạn không muốn cho phép con trỏ đến các thành viên, vì vậy
std::result_of_t
sẽ thành công trong trường hợp bạn có thể muốn nó không thành công.
‡ Với các trường hợp ngoại lệ. Mặc dù nó hỗ trợ con trỏ đến các thành viên, nhưng result_of
sẽ không hoạt động nếu bạn cố gắng khởi tạo một type-id không hợp lệ . Chúng sẽ bao gồm một hàm trả về một hàm hoặc nhận các kiểu trừu tượng theo giá trị. Ví dụ.:
template <class F, class R = result_of_t<F()>>
R call(F& f) { return f(); }
int answer() { return 42; }
call(answer); // nope
Cách sử dụng chính xác sẽ là như vậy result_of_t<F&()>
, nhưng đó là một chi tiết bạn không cần phải nhớ decltype
.
decltype
là xấu hơn nhưng cũng mạnh mẽ hơn.result_of
chỉ có thể được sử dụng cho các kiểu có thể gọi được và nó yêu cầu các kiểu làm đối số. Ví dụ, bạn không thể sử dụngresult_of
ở đây:template <typename T, typename U> auto sum( T t, U u ) -> decltype( t + u );
nếu các đối số có thể là các kiểu số học (không có hàmF
nào để bạn có thể xác địnhF(T,U)
để biểu diễnt+u
. Đối với các kiểu do người dùng xác định, bạn có thể làm theo cách tương tự (tôi chưa thực sự chơi với nó) rằng các cuộc gọi đến các phương pháp thành viên có thể là khó để làm vớiresult_of
mà không sử dụng chất kết dính hoặc lambdas