我正在围绕框架编写一个软件,并且我正在使用的类(准确地说是扩展类)在更高版本中已重命名。有什么方法可以在C ++ 11中编写一些宏/模板,以确定是否在代码中声明了具有特定名称的类?
下面是我要完成的任务的说明。
假设文件class_include.h包含了类A
的定义:
class A
{
...
};
或类B
:
class B
{
...
};
和类C
尝试扩展声明的任何内容:
#include <class_include.h>
#if (class A is declared)
class C : public A
#else // class B is declared
class C : public B
#endif
{
...
};
注意:我想到了尝试检查框架的版本,但是这个问题的答案令我感兴趣。我也无法更改任何框架头文件。
编辑:可接受的答案取决于是否定义了该类(这意味着声明),而就我而言,该类是在定义了该类的情况下声明的。
答案 0 :(得分:12)
除了已经给出的模板魔术思想外,传统的方法是在可能的情况下使用库的“版本”宏。如果没有,您难道不能只是更改代码并开始使用新版本的库吗?适当地在构建系统中表示依赖关系的新版本。
最终,依赖关系控制是软件部署过程中正常且预期的部分。因此,即使这可能会有点麻烦,但我不会为了完全消除它而使您的代码过于复杂。我的意思是,您已经必须以某种形式将库作为依赖项列出,因此您甚至还没有开始就已经走了一半!
只要我们认为“定义了A类”就可以等同于“定义了A类并采取我们认为应该采用的形式”,其他答案在技术上就可以实现您的目标。 。如果没有有效的依赖项控制,您就已经不知所措,并且不需要黑客。
答案 1 :(得分:11)
可以,并且不需要宏。首先观察一下,即使可以使用完整的定义,也可以“转发”声明一个类。即这是有效的:
class foo{};
class foo;
现在,借助自制void_t
实现和is_complete
类型的实用程序,您可以执行以下操作:
#include <type_traits>
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
template <typename T, typename Enabler = void>
struct is_complete : std::false_type {};
template <typename T>
struct is_complete<T, ::void_t<decltype(sizeof(T) != 0)>> : std::true_type {};
class A;
class B;
class C : public std::conditional<is_complete<A>::value, A, B>::type {
};
根据是否存在A
的完整定义,C
将公开继承自A
或B
。 See a live example。
但是我告诫,这需要小心处理,否则您的程序中很可能会违反ODR。
答案 2 :(得分:4)
一种方法是使用typeid
利用SFINAE,其结果与不完整的类型会有所不同:
#include <iostream>
#include <typeinfo> // for typeid
template<typename T, typename = void>
constexpr bool is_defined = false;
template<typename T>
constexpr bool is_defined<T, decltype(typeid(T), void())> = true;
struct complete {}; // i.e. `complete` is defined.
struct incomplete; // not defined, just a forward declaration
int main()
{
std::cout << is_defined<complete> << " " << is_defined<incomplete>;
}
尽管这要求您转发声明的类,但是由于is_defined
是constexpr
,因此可以在编译时使用。您也可以使用sizeof
,但我对空基类优化会产生误报感到不安。
答案 3 :(得分:3)
在您的情况下,由于您要从类继承,因此必须声明它也要对其进行定义;这样就简化了很多事情。
SELECT *
FROM dba_users a
WHERE USER_ID
NOT
IN (SELECT ID
FROM LCM.TBL_INT_PERSON b
where b.id = a.User_id);
这通过尝试在请求的类型上使用namespace detail_detectDefinedClass {
template <class Void, class First, class... Rest>
struct detect : detect<Void, Rest...> { };
template <class First, class... Rest>
struct detect<std::void_t<decltype(sizeof(First))>, First, Rest...> {
using type = First;
};
}
template <class... Classes>
using DetectDefinedClass = typename detail_detectDefinedClass::detect<
std::void_t<>, Classes...
>::type;
struct A /* B */ {
};
class C : public DetectDefinedClass<struct A, struct B> {
};
static_assert(std::is_base_of_v<A, C>);
//static_assert(std::is_base_of_v<B, C>);
来使用SFINAE,这仅在定义了类型(模板的参数列表中的sizeof
仅声明它)的情况下有效。
答案 4 :(得分:2)
您可以使用类代码保护:
//classA.h
#ifndef cgA
#define cgA
class A {};
#endif
//classB.h
#ifndef cgB
#define cgB
class B {};
#endif
//classC.h
#include classA.h
#include classB.h
#ifdef cgA
class C : public A
#else
class C : public B
#endif
使用额外的注释信息进行编辑:您可以添加一个空的classA.h和classB.h,并在includepath中最后引用它们。如果您的框架不包含A或B,则将加载空文件。
答案 5 :(得分:2)
您可以使用类型别名,然后根据库版本选择正确的别名
#ifdef LIB_VER_123
typedef A A1;
#else
typedef B A1;
#endif
class C : public A1
{
...
}