拒绝包含某些公共静态数据成员的类型

Reject types that contain certain public static data members

本文关键字:数据成员 类型 静态 包含某 拒绝      更新时间:2023-10-16

我想写一个模板函数,拒绝具有某些公共静态数据成员的类型。

实现这一点的一种方法是在所有不需要的成员上使用is_detected

我的代码:

#include <experimental/type_traits>
template <typename T>
using DetectA = decltype(T::A);
template <typename T>
using DetectB = decltype(T::B);
template <typename T>
void foo()
{
static_assert(
!std::experimental::is_detected<DetectA, T>::value);
static_assert(
!std::experimental::is_detected<DetectB, T>::value);
}
struct U
{
static constexpr auto A = false;
static constexpr auto B = true;
};
struct V
{
static constexpr auto C = false;
static constexpr auto D = true;
};
void bar()
{
// foo<U>();  // <- Throws compiler error as it should
foo<V>();
}

正如你所能想象的,如果我有更多的成员要拒绝,代码很快就会变得重复。如果我有一个单独的检测操作告诉我数据成员的any是否存在,那么我应该能够绕过这个限制。

请注意,下面的代码不起作用,因为如果存在any成员而不是all,我想拒绝。

template <typename T>
using DetectAorB = decltype(T::A, T::B);  // <- This is and, not or
template <typename T>
void baz()
{
static_assert(
!std::experimental::is_detected<DetectAorB, T>::value);
}
struct W
{
static constexpr auto B = false;
};
void qux()
{
// baz<U>();
baz<V>();
baz<W>();  // <- This shouldn't compile
}

我该如何实现这种行为?使用is_detected不是必要的,但了解如何检测此类病例是有帮助的。我使用C++17(以防这些信息有帮助(。

让我们将std::disjunction用于"OR"逻辑和变量模板:

template<class T>
using detect_a = decltype(T::a);
template<class T>
using detect_b = decltype(T::b);
template<class T, template<class> class detector, typename = void>
struct is_detected : std::false_type {};
template<class T, template<class> class detector>
struct is_detected<T, detector, std::void_t<detector<T>>> : std::true_type {};
template<class T, template<class> class... detectors>
struct is_any_detected : std::disjunction<is_detected<T, detectors>...> {};
template<class T>
void foo() {
static_assert(!is_any_detected<T, detect_a, detect_b>::value);
}

然后:

struct U {
static constexpr auto a = false;
static constexpr auto b = true;
};
struct V {
static constexpr auto c = false;
static constexpr auto d = true;
};
struct W {
static constexpr auto b = false;
};
foo<U>(); // doesn't compile
foo<V>(); // compiles
foo<W>(); // doesn't compile

演示

模板的棘手之处在于,替身必须是真实的类型名称或值,而没有membername。因此,这种方法总会有一些样板。

宏没有这个限制,但它们不适合,因为可变宏不能是递归的。但是,就像使用旧的C++98模板一样,您仍然可以制作"最多N个"解决方案。我尝试生成最多4张会员支票。如果你需要更多,你可以拉伸这个图案。

#include <type_traits>
#include <utility>
#define CHECK_MEMBER( type, member, member_type ) 
template <typename _T, typename = void> 
struct __has_##type##member : std::false_type { }; 
template <typename _T> 
struct __has_##type##member<_T, std::enable_if_t< std::is_same_v<member_type*, decltype( &_T::member )>>> : std::true_type { }; 
static_assert(!__has_##type##member<type>(), "forbidden " #member_type " member '" #member "' in type '" #type "'");
#define _CHECK_MEMBER_1( _1 ) static_assert(false, "Expected at least 3 arguments, found 1")
#define _CHECK_MEMBER_2( _1, _2 ) static_assert(false, "Expected at least 3 arguments, found 2")
#define _CHECK_MEMBER_3( _1, _2, _3 ) CHECK_MEMBER(_1, _2, _3)
#define _CHECK_MEMBER_4( _1, _2, _3, _4 ) static_assert(false, "Expected odd number of arguments, found 4")
#define _CHECK_MEMBER_5( _1, _2, _3, _4, _5 ) CHECK_MEMBER(_1, _2, _3) CHECK_MEMBER(_1, _4, _5)
#define _CHECK_MEMBER_6( _1, _2, _3, _4, _5, _6 ) static_assert(false, "Expected odd number of arguments, found 6")
#define _CHECK_MEMBER_7( _1, _2, _3, _4, _5, _6, _7 ) CHECK_MEMBER(_1, _2, _3) CHECK_MEMBER(_1, _4, _5) CHECK_MEMBER(_1, _6, _7)
#define _CHECK_MEMBER_8( _1, _2, _3, _4, _5, _6, _7, _8 ) static_assert(false, "Expected odd number of arguments, found 8")
#define _CHECK_MEMBER_9( _1, _2, _3, _4, _5, _6, _7, _8, _9 ) CHECK_MEMBER(_1, _2, _3) CHECK_MEMBER(_1, _4, _5) CHECK_MEMBER(_1, _6, _7) CHECK_MEMBER(_1, _8, _9)
#define _GET_CHECK_MEMBER(_1,_2,_3,_4,_5,_6,_7,_8,_9,NAME,...) NAME
#define CHECK_MEMBERS(...) _GET_CHECK_MEMBER(__VA_ARGS__, _CHECK_MEMBER_9, _CHECK_MEMBER_8, _CHECK_MEMBER_7, _CHECK_MEMBER_6, _CHECK_MEMBER_5, _CHECK_MEMBER_4, _CHECK_MEMBER_3, _CHECK_MEMBER_2, _CHECK_MEMBER_1)(__VA_ARGS__)

用法:

struct BadType { static int bad1; static bool* bad2; static double** bad3; static double bad4; };
struct OkNotStaticType { int bad1; bool* bad2; double** bad3; double bad4; };
struct OkDifferentType { double bad1; void* bad2; char* bad3; float bad4; };
struct OkUnrelatedType { bool good; };
#define CHECK_FORBIDDEN(T) CHECK_MEMBERS(T, bad1, int, bad2, bool*, bad3, double**, bad4, double)
CHECK_FORBIDDEN(BadType) // "forbidden int member 'bad1' in type 'BadType'"
// "forbidden bool* member 'bad2' in type 'BadType'"
// "forbidden double** member 'bad3' in type 'BadType'"
// "forbidden double member 'bad4' in type 'BadType'"
CHECK_FORBIDDEN(OkNotStaticType)
CHECK_FORBIDDEN(OkDifferentType)
CHECK_FORBIDDEN(OkUnrelatedType)

示例:

https://godbolt.org/z/-bpbTg