boost 的惰性评估 eval_if
lazy evaluation of boost eval_if
我想我对 eval_if 的懒惰有误解。
以下代码调整自众所周知的成员检测机制,
我尝试添加成员也应该可以转换为特定类型的功能。
template <typename T, typename P = size_t>
struct has_height
{
private:
using Yes = char[2];
using No = char[1];
struct Fallback
{
P height;
};
struct Derived : T, Fallback
{
};
template <class U>
static No &test(decltype(U::height) *);
template <typename U>
static Yes &test(U *);
public:
static constexpr bool has_member = sizeof(test<Derived>(nullptr)) == sizeof(Yes);
using value_type = std::integral_constant<bool, has_member>;
static constexpr bool value = boost::mpl::eval_if<
value_type,
std::is_convertible<decltype(T::height), P>,
boost::mpl::identity<std::false_type>>::type::value;
};
template <typename T, typename P>
const bool has_height<T, P>::value;
template <typename T, typename P>
const bool has_height<T, P>::has_member;
但是当执行以下操作时:
struct TypeA
{
size_t width;
;
struct TypeB
{
size_t no_width;
};
EXPECT_TRUE((has_width<TypeA, size_t>::value));
EXPECT_TRUE((has_width<TypeB, size_t>::value));
我收到以下错误:
/home/auke/git/base/sdk/src/horus/cmp/prop/reflection/has_width.hpp:49: error: ‘width’ is not a member of refl_test_reflection_types_Test::TestBody()::TypeB’
static constexpr bool value = boost::mpl::eval_if<
^~~~~
我明白编译器在说什么,只是不知道为什么。我的假设
这是由于条件为假(已测试),因此未评估第二个参数(std::is_convertible)。
任何关于此事的澄清将不胜感激,可能的修复也很好 ;)
奥克
如果您暂时忘记了 boost::mpl
,请看一下这个的简化形式:
eval_if<value_type, A, B>::type::value
应该更清楚的是,A
和B
都被求值成为第二个和第三个模板参数,必须求值。这样就不会偷懒了。
如果你想实现 "lazy" 行为,你可以使用模板专业化。
template<bool, class T, class P>
struct has_height_helper {
// `true` case. Evaluate `T::height`
static constexpr bool value = std::is_convertible<decltype(T::height), P>::value;
}
template<class T, class P>
struct has_height_helper<false, T, P> {
static constexpr bool value = false;
}
// And now use the helper in the class
static constexpr bool value = has_height_helper<has_member, T, P>::value;
作为模板规范,基本上 "hides" 代码取决于使用的规范。
另一种选择是使用带有 if constexpr
的 constexpr 函数来隐藏代码:
template<bool has_member, class T, class P>
constexpr bool has_height_helper() {
if constexpr (has_member) {
return std::is_convertible<decltype(T::height), P>::value;
} else {
return false;
}
}
你可以把它放到结构中:
template <typename T, typename P = size_t>
struct has_height
{
private:
struct Fallback
{
P height;
};
struct Derived : T, Fallback
{
};
template <class U>
constexpr static bool test(decltype(U::height) *) {
return std::is_convertible<decltype(T::height), P>::value;
}
template <typename U>
constexpr static int test(U *) {
// Basically return any non-bool false so we can tell the difference
return 0;
}
public:
static constexpr bool has_member = std::is_same<test<Derived>(nullptr), bool>::value;
using value_type = std::integral_constant<bool, has_member>;
static constexpr bool value = test<Derived>(nullptr);
};
我想我对 eval_if 的懒惰有误解。
以下代码调整自众所周知的成员检测机制, 我尝试添加成员也应该可以转换为特定类型的功能。
template <typename T, typename P = size_t>
struct has_height
{
private:
using Yes = char[2];
using No = char[1];
struct Fallback
{
P height;
};
struct Derived : T, Fallback
{
};
template <class U>
static No &test(decltype(U::height) *);
template <typename U>
static Yes &test(U *);
public:
static constexpr bool has_member = sizeof(test<Derived>(nullptr)) == sizeof(Yes);
using value_type = std::integral_constant<bool, has_member>;
static constexpr bool value = boost::mpl::eval_if<
value_type,
std::is_convertible<decltype(T::height), P>,
boost::mpl::identity<std::false_type>>::type::value;
};
template <typename T, typename P>
const bool has_height<T, P>::value;
template <typename T, typename P>
const bool has_height<T, P>::has_member;
但是当执行以下操作时:
struct TypeA
{
size_t width;
;
struct TypeB
{
size_t no_width;
};
EXPECT_TRUE((has_width<TypeA, size_t>::value));
EXPECT_TRUE((has_width<TypeB, size_t>::value));
我收到以下错误:
/home/auke/git/base/sdk/src/horus/cmp/prop/reflection/has_width.hpp:49: error: ‘width’ is not a member of refl_test_reflection_types_Test::TestBody()::TypeB’
static constexpr bool value = boost::mpl::eval_if<
^~~~~
我明白编译器在说什么,只是不知道为什么。我的假设 这是由于条件为假(已测试),因此未评估第二个参数(std::is_convertible)。
任何关于此事的澄清将不胜感激,可能的修复也很好 ;)
奥克
如果您暂时忘记了 boost::mpl
,请看一下这个的简化形式:
eval_if<value_type, A, B>::type::value
应该更清楚的是,A
和B
都被求值成为第二个和第三个模板参数,必须求值。这样就不会偷懒了。
如果你想实现 "lazy" 行为,你可以使用模板专业化。
template<bool, class T, class P>
struct has_height_helper {
// `true` case. Evaluate `T::height`
static constexpr bool value = std::is_convertible<decltype(T::height), P>::value;
}
template<class T, class P>
struct has_height_helper<false, T, P> {
static constexpr bool value = false;
}
// And now use the helper in the class
static constexpr bool value = has_height_helper<has_member, T, P>::value;
作为模板规范,基本上 "hides" 代码取决于使用的规范。
另一种选择是使用带有 if constexpr
的 constexpr 函数来隐藏代码:
template<bool has_member, class T, class P>
constexpr bool has_height_helper() {
if constexpr (has_member) {
return std::is_convertible<decltype(T::height), P>::value;
} else {
return false;
}
}
你可以把它放到结构中:
template <typename T, typename P = size_t>
struct has_height
{
private:
struct Fallback
{
P height;
};
struct Derived : T, Fallback
{
};
template <class U>
constexpr static bool test(decltype(U::height) *) {
return std::is_convertible<decltype(T::height), P>::value;
}
template <typename U>
constexpr static int test(U *) {
// Basically return any non-bool false so we can tell the difference
return 0;
}
public:
static constexpr bool has_member = std::is_same<test<Derived>(nullptr), bool>::value;
using value_type = std::integral_constant<bool, has_member>;
static constexpr bool value = test<Derived>(nullptr);
};