我正在以C ++ 17风格在SFINAE上“玩”一点,我试图获得这样的结果:替换失败而不是仅仅导致编译器移至下一种情况,而是导致“编译器”(相反,经过此类尝试的类)(通过false
/ std::false_type
)报告替换失败,然后编译器移至下一种情况。
我目前的尝试归结为这样的事情:
template <typename T>
class logic_value
{
static constexpr bool result = std::is_same<
std::enable_if_t<T, std::true_type>,
std::true_type>;
};
template <typename T>
inline constexpr bool lv = logic_value<T>::result;
(用法示例:)
template <typename T>
std::enable_if_t<
lv<decltype(std::declval<T>() + std::declval<T>())> /* has plus operator */
&&
(lv<decltype(std::declval<T>().func_foo())> || lv<decltype(std::declval<T>().func_bar())>) /*has either func_foo() or func_bar() */
&&
lv<T&> /* can have a reference */
&&
(!lv<decltype(std::declval<T>().func_foobar())>) /* does NOT have a func_foobar() */
, T> const & Ioo(T const &);
但是它不像我想象的那样起作用...:/
当我使用logic not
(!
)运算符来确保测试类型中的某些内容不存在时,尤其棘手。
答案 0 :(得分:3)
执行此操作的方法是使用检测习惯用法,您希望is_detected
具有适当的别名。
在您的特定情况下:
template <typename T> using has_plus_t = decltype(std::declval<T>() + std::declval<T>());
template <typename T> using func_foo_t = decltype(std::declval<T>().func_foo());
template <typename T> using func_bar_t = decltype(std::declval<T>().func_bar());
template <typename T> using func_foobar_t = decltype(std::declval<T>().func_foobar());
template <typename T>
enable_if_t<
is_detected_v<has_plus_t, T>
&&
(is_detected_v<func_foo_t, T> || is_detected_v<func_bar_t, T>)
&&
is_detected_v<add_lvalue_reference_t, T>
&&
!is_detected_v<func_foobar_t, T>
, T> const & Ioo(T const &);
您无法在这种情况下拥有要测试的表达式,因为一旦失败,整个事情就会失败。您的逻辑允许并要求某些表达式失败-因此,您需要控制所有表达式的实例化这些表达式。这就是检测惯用语的目的。
答案 1 :(得分:2)
logical_value
的问题在于,在发生替换错误的情况下,您没有为编译器提供替代路径。
我是这样做的:
#include <utility>
#include <array>
template<typename T, typename U>
constexpr auto check_addition(int) -> decltype(std::declval<T>() + std::declval<U>() , std::true_type{});
template<typename T, typename U>
constexpr std::false_type check_addition(...);
template<typename T, typename U>
constexpr bool can_add = decltype(check_addition<T, U>(0))::value;
int main() {
static_assert(can_add<int, float>);
static_assert(!can_add<std::string, float>);
}
这个想法是利用两个重载,一个重载特定类型的参数(在我的情况下为int
),另一个重载省略号。当我们使用int
作为参数调用该重载函数时,编译器将首先在必须执行所需检查的地方检查int
重载。
逗号运算符用于提供true_type
作为返回类型,在强制转换检查成功完成时。
如果第一个重载SFINAE失败,则选择第二个重载,该重载始终返回false_type
。