首页 > 解决方案 > 是否可以将带有字符串化运算符的宏转换为 constexpr?

问题描述

我编写了以下宏来模仿 C# 的nameof运算符,但在 C++/CLI 中:

#define nameof(x) (#x)

if (info == nullptr)
    throw gcnew ArgumentNullException(nameof(info));

我试图将此宏转换为constexpr

其中任何一个都是错误的,它们返回的内容是ToString

template <typename T>
constexpr auto NAMEOF1(T value)
{
    return """" + value + """";
}

template <typename T>
constexpr auto NAMEOF2(T value)
{
    return System::String::Format("{0}", value);
}

第三次尝试,使用typeid关键字:

template <typename T>
constexpr auto NAMEOF3(T value)
{
    return gcnew System::String(typeid(value).name());
}

但它失败并出现以下错误:

错误 C3185:“typeid”:用于托管类型“T”,请改用“T::typeid”

简而言之,并不像听起来那么容易。

问题:

是否可以将此nameof宏转换为 a constexpr

(或者我应该坚持旧的#define?)

标签: c++stringc++-cliconstexprpreprocessor-directive

解决方案


不,不可能在constexpr函数中获取 C++ 中对象的名称。constexpr函数仍然可以在运行时被调用,并且在运行时,这种反射是不可能的。实际上,一般 C++ 中的反射是极其有限的。如果没有宏,您的反射能力将仅限于使用肮脏和不直观的模板技巧来测试成员的存在和类型,但不会将名称作为字符串返回。另请注意,一旦将参数传递给类似的函数NAMEOF1,该参数的名称将始终"value". 无法查询调用者的范围以获取传递的名称或表达式。

你应该坚持你的解决方案:

#define nameof(x) (#x)

当然,这也是一个非常有限的解决方案。这将简单地将表达式x完全逐字转换为字符串,并且没有nameofC# 中不同实体和范围的概念。

我说这来自非托管 C++ 背景。从您收到的错误消息中,很明显托管 C++ 对typeid. 也许对 C++/CLI 有更多了解的人可以启发我或给出更好的答案。

或者,如果这种空检查是一种常见的模式,您可以将其包装成一个更简洁的宏。例如:

define THROW_IF_NULL(x) \
    if ((x) == nullptr){ \
        throw gcnew ArgumentNullException(#x); \
    }

示例用法:

void process_info(Info* info){
    THROW_IF_NULL(info)
    // --snip--
}


另一方面,我不确定您希望这样做:

return """" + value + """";

但如果我的理解是正确的,则表达式""""将被解析为两个空字符串文字("", ""),因为它们并排出现,所以预处理器将它们附加到单个空字符串文字中。因此,您可以替换""""""以达到相同的效果。但也许你想要别的东西?


推荐阅读