您在使用指針的成員函數是一個壞主意;如果foo
被重載,它虛假地失敗(你有一個foo,但不只是一個)。誰真的想要「你有一個富有」?幾乎沒有人。
這裏是一個簡短的版本:
template<class T>
using dot_foo_r = decltype(std::declval<T>().foo());
template<class T>
using can_foo = can_apply<dot_foo_r, T>;
其中
namespace details {
template<template<class...>class, class, class...>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;
現在,寫dot_foo_r
是有點討厭。
隨着constexpr
lambda我們可以使它更少討厭,並做到內聯。
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
它確實需要RETURNS
宏,至少要等到@巴里的提交[](auto&&f)RETURNS(f())
相當於[](auto&&f)=>f()
。
然後,我們寫can_invoke_f
,這是一個constexpr
變種std::is_invokable
:
template<class F>
constexpr auto can_invoke(F&& f) {
return [](auto&&...args)->std::is_invokable<F(decltype(args)...)>{
return {};
};
}
這給我們:
if constexpr(
can_invoke([](auto&&var) RETURNS(var.foo()))(var)
) {
var.foo();
}
或使用@巴里提出的C++ 20語法:
if constexpr(can_invoke(var=>var.foo())(var)) {
var.foo();
}
我們完成了。
訣竅是,RETURNS
宏(或=>
C++ 20功能)讓我們對錶達式執行SFINAE。拉姆達變成了一種簡單的方式來表達這個表達式作爲一個值。
你可以寫
[](auto&&var) ->decltype(var.foo()) { return var.foo(); }
,但我認爲RETURNS
是值得的(我不喜歡宏)。
[相關](https://meta.stackoverflow.com/a/323382/2069064)。一個答案可能是因爲這裏沒有替代,所以SFINAE如何?另一個答案可能是因爲它沒有被考慮,因爲提案總是關於'如果'採取'bool'? – Barry
@Barry希望將問題編輯成可以有明確答案的表單。原來是想[順着這樣的答案](https://stackoverflow.com/a/6623089/4832499) –