是否可以将带有字符串化运算符的宏转换为 constexpr?

Is it possible to convert a macro with stringizing operator to a constexpr?

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

#define nameof(x) (#x)

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

我已尝试将此宏转换为 constexpr:

这两个都是错误的,它们returnToString的内容:

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 宏转换为 constexpr

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

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

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

#define nameof(x) (#x)

这当然也是一个非常有限的解决方案。这将简单地将表达式 x 完美地逐字转换为字符串,并且没有像 C# 中的 nameof 那样的不同实体和作用域的概念。

我说这是来自非托管 C++ 背景。从您收到的错误消息中,很明显托管 C++ 对 typeid 产生了有趣的影响。或许对C++/CLI了解更多的人可以赐教或给出更好的答案。

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

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

用法示例:

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


另一方面,我不确定你希望它做什么:

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

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