gpt4 book ai didi

C++ 预处理器测试类成员是否存在

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:22:59 25 4
gpt4 key购买 nike

是否有等效的 #ifdef 来测试成员是否存在于类中,以便可以在不导致代码无法通过编译器的情况下完成处理。我尝试过模板操作,但特定问题没有成功。

例如

#if member baseclass.memberA()
baseclass.memberA().push_back(data);
#else
doAlternate(data);
#endif

显然上述内容无效,但我试图发现是否已将此类内容添加到 C++11

请注意,在初始设置中,将存在 memberA、memberB、memberC、...每个都需要 push_back。其他成员将来会被添加到基类中,这就是为什么我要创建一个模板,以便即使当前基类没有某些成员(例如memberX),所有案例也会正确编译和处理。否则,我可以使用一个非常简单的模板将 push_back() 行放入。

这实际上是最简单的情况。还有一种情况是,我创建了子类的实例化,然后将其推回到子类成员中。
// Instantiate an element of the Maindata class
::basedata::Maindata maindata;
//Instantiate an element of the Subdata class
::basedata::Subdata subinfo("This goes into the subinfo vector");
// Process some data that is part of the Subdata class
subinfo.contexts(contextInfo);
// Push the instantiated Subdata into the Subdata member of Maindata
maindata.subdata().push_back(subinfo);

请注意,需要设置 Subdata 和 subdata() 以便实现适当的代码。但是,如果::basedata::Subdata 存在,那么 maindata.subdata() 也会存在。

我已经使用模板尝试了各种方法,但收到的各种答案都无法解决特定问题。示例是 template instantiation check for member existing in class , C++ class member check if not a template , C++ template for variable type declaration

最佳答案

这只是 void_t 的另一个案例.

我们需要一个小 helper 模板Void并定义一个方便的模板类型别名 void_t .

#include <type_traits>

template<typename...>
struct Void { using type = void; };

template<typename... T>
using void_t = typename Void<T...>::type;

我们定义了实现回退策略的主要模板。
template<typename T, typename = void>
struct Helper
{
static void
function(T& t)
{
std::cout << "doing something else with " << &t << std::endl;
}
};

并为支持特定操作的类型提供部分特化,在本例中为 .data().push_back(int) .
template<typename T>
struct Helper<T, void_t<decltype(std::declval<T>().data().push_back(0))>>
{
static void
function(T& t)
{
std::cout << "pushing back data to " << &t << std::endl;
t.data().push_back(42);
}
};

隐藏 Helper来自我们客户的实现细节并允许对模板参数进行类型推导,我们可以很好地包装它。
template<typename T>
void
function(T& t)
{
Helper<T>::function(t);
}

这就是我们的客户使用它的方式。
#include <iostream>
#include <vector>

class Alpha
{
public:
std::vector<int>& data() { return this->data_; }
private:
std::vector<int> data_ {};
};

class Beta { /* has no data() */ };

int
main()
{
Alpha alpha {};
Beta beta {};
std::cout << "&alpha = " << &alpha << std::endl;
std::cout << "&beta = " << &beta << std::endl;
function(alpha);
function(beta);
}

可能的输出:

&alpha = 0x7ffffd2a3eb0
&beta = 0x7ffffd2a3eaf
pushing back data to 0x7ffffd2a3eb0
doing something else with 0x7ffffd2a3eaf

更新:如何将此技术应用于多个成员

上面显示的技术可以应用于任意数量的成员。让我们做一个小例子。假设我们要编写一个模板函数 frobnicate这需要一个泛型类型的参数,如果对象有...
  • ...成员函数incr不带参数的,调用它,
  • ...数据成员 name , 如果可能的话,附加一些文本和
  • ...数据成员 numbers , push_back如果可能的话,给它一些数字。

  • 我真的建议你通过实现三个助手来解决这个问题 struct s 如上所示。它没有那么多多余的输入,并且可以使代码更清晰。

    但是,如果您想忽略此建议,让我们看看如何通过使用宏来减少输入。假设 void_t 的定义相同如上所示,我们可以定义如下宏。
    #define MAKE_SFINAE_HELPER(NAME, TYPE, OPERATION, ARGS, CODE)           \
    template<typename TYPE, typename = void> \
    struct NAME \
    { \
    template<typename... AnyT> \
    void \
    operator()(AnyT&&...) noexcept \
    { \
    /* do nothing */ \
    } \
    }; \
    \
    template<typename TYPE> \
    struct NAME<TYPE, void_t<decltype(std::declval<TypeT>()OPERATION)>> \
    { \
    void operator()ARGS noexcept(noexcept(CODE)) \
    { \
    CODE; \
    } \
    };

    它将定义一个 structNAME在类型参数上模板化 TYPE并使用运算符 () 定义主模板它接受任意数量的任何类型的参数并且绝对不执行任何操作。如果不支持所需的操作,这将用作后备。

    但是,如果类型为 TYPE 的对象支持操作 OPERATION ,然后是带有运算符 () 的偏特化接受参数 ARGS并执行 CODE将会被使用。宏定义为 ARGS可以是带括号的参数列表。不幸的是,预处理器语法只允许将单个表达式作为 CODE 传递。 .这不是一个大问题,因为我们总是可以编写一个委托(delegate)给另一个函数的函数调用。 (请记住,计算机科学中的任何问题都可以通过添加额外的间接级别来解决——当然,间接级别过多的问题除外……)运算符 ()将声明部分特化的 noexcept当且仅当 CODE是。 (这也仅适用于 CODE 仅限于单个表达式。)

    运营商 ()的原因因为主模板是一个模板,否则编译器可能会发出有关未使用变量的警告。当然,您可以更改宏以接受附加参数 FALLBACK_CODE放置在主模板运算符的主体中 ()应该使用相同的 ARGS然后。

    在最简单的情况下,可以结合使用 OPERATIONCODE参数为一个,然后 CODE不能引用 ARGS这有效地限制了 ARGSTYPE 类型的单个参数在这种情况下,如果您不需要灵活性,您也可以去掉该参数。

    因此,让我们将其应用于我们的问题。首先,我们需要一个辅助函数来推回数字,因为这不能写成(至少,让我们假装这样)作为单个表达式。我使这个函数尽可能通用,只对成员名称进行假设。
    template<typename ObjT, typename NumT>
    void
    do_with_numbers(ObjT& obj, NumT num1, NumT num2, NumT num3)
    {
    obj.numbers.push_back(num1);
    obj.numbers.push_back(num2);
    obj.numbers.push_back(num3);
    }

    由于其他两个所需的操作可以很容易地写成一个单独的表达式,我们不需要对它们进行进一步的间接操作。所以现在,我们可以生成我们的 SFINAE 助手。
    MAKE_SFINAE_HELPER(HelperIncr,
    TypeT,
    .incr(),
    (TypeT& obj),
    obj.incr())

    MAKE_SFINAE_HELPER(HelperName,
    TypeT,
    .name += "",
    (TypeT& obj, const std::string& appendix),
    obj.name += appendix)

    MAKE_SFINAE_HELPER(HelperNumbers,
    TypeT,
    .numbers.push_back(0),
    (TypeT& obj, int i1, int i2, int i3),
    do_with_numbers(obj, i1, i2, i3))

    配备了这些,我们终于可以写我们的 frobnicate功能。这真的很简单。
    template<typename T>
    void
    frobnicate(T& object)
    {
    HelperIncr<T>()(object);
    HelperName<T>()(object, "def");
    HelperNumbers<T>()(object, 4, 5, 6);
    }

    为了看到一切正常,让我们制作两个 struct s 部分支持相关操作。
    #include <string>
    #include <vector>

    struct Widget
    {
    std::vector<int> numbers {1, 2, 3};
    int counter {};
    void incr() noexcept { this->counter += 1; }
    };

    struct Gadget
    {
    std::string name {"abc"};
    int counter {};
    void incr() noexcept { this->counter += 1; }
    };

    由于我想打印它们,让我们也定义运算符 << .
    #include <iostream>

    std::ostream&
    operator<<(std::ostream& os, const Widget& w)
    {
    os << "Widget : { counter : " << w.counter << ", numbers : [";
    int i {};
    for (const auto& v : w.numbers)
    os << (i++ ? ", " : "") << v;
    os << "] }";
    return os;
    }

    std::ostream&
    operator<<(std::ostream& os, const Gadget& g)
    {
    os << "Gadget : { counter : " << g.counter << ", "
    << "name = \"" << g.name << "\" }";
    return os;
    }

    然后我们开始:
    int
    main()
    {
    Widget widget {};
    Gadget gadget {};
    std::cout << widget << "\n" << gadget << "\n\n";
    frobnicate(widget);
    frobnicate(gadget);
    std::cout << widget << "\n" << gadget << "\n";
    }

    输出:

    Widget : { counter : 0, numbers : [1, 2, 3] }
    Gadget : { counter : 0, name = "abc" }

    Widget : { counter : 1, numbers : [1, 2, 3, 4, 5, 6] }
    Gadget : { counter : 1, name = "abcdef" }

    我鼓励您仔细衡量这种宏观方法的成本和 yield 。在我看来,额外的复杂性几乎不值得在打字上节省一点点。

    关于C++ 预处理器测试类成员是否存在,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28748868/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com