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_ofchỉ 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..)>::typevà decltype(std::declval<F>()(std::declval<Args>()...)là tất cả về điều đó INVOKE. Sử dụng declval/ decltypetrực tiếp, ngoài việc nhập lâu hơn một chút, chỉ hợp lệ nếu Fcó 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_ofbổ 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_ofcó 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_ofbâ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 Fs ‡ 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_tsẽ 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_ofsẽ 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.
decltypelà xấu hơn nhưng cũng mạnh mẽ hơn.result_ofchỉ 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àmFnà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_ofmà không sử dụng chất kết dính hoặc lambdas