如何检查是否在C ++中声明了一个类?

时间:2018-11-14 10:17:57

标签: c++ c++11

我正在围绕框架编写一个软件,并且我正在使用的类(准确地说是扩展类)在更高版本中已重命名。有什么方法可以在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
{
...
};

注意:我想到了尝试检查框架的版本,但是这个问题的答案令我感兴趣。我也无法更改任何框架头文件。

编辑:可接受的答案取决于是否定义了该类(这意味着声明),而就我而言,该类是在定义了该类的情况下声明的。

6 个答案:

答案 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将公开继承自ABSee 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_definedconstexpr,因此可以在编译时使用。您也可以使用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
{ 
   ...
}