拒绝包含某些公共静态数据成员的类型
Reject types that contain certain public static data members
我想写一个模板函数,拒绝具有某些公共静态数据成员的类型。
实现这一点的一种方法是在所有不需要的成员上使用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
相关文章:
- int数据类型的指针指向的是什么,如果是一个类的私有数据成员,我们创建了该类的两个对象?
- 在类 A 中创建类型为 B 类的向量 - 访问数据 [C++] [成员在两个类中都是私有的]
- 是否可以根据其数据成员的类型确定类型的大小
- 拒绝包含某些公共静态数据成员的类型
- 复制构造抽象类型的数据成员
- 引用类型的数据成员提供有关恒常正确性"loophole"
- 如果类在 C++ 中具有常量或引用类型的非静态数据成员,为什么编译器不提供默认赋值运算符?
- 具有引用数据成员的结构不是文本类型吗?
- 类数据成员指针的非类型模板参数包无法使用 gcc 编译
- 标记为 [数据成员] 时的意外类型'USN'
- 依赖于依赖类型的非静态数据成员的非限定名称
- 基类数据成员类型取决于派生类
- 类型为专用数据成员的静态成员
- QString:包括非文本类型的静态数据成员的初始化
- 由于标准而指向 Base 数据成员的指针类型
- sizeof参考对于类型和数据成员的不同
- 参考类型静态数据成员的问题可能是编译器错误
- 指向不完整类型的数据成员
- 联合类型的数据成员的初始化
- 基于另一个数据成员的类数据成员类型