这种立即使用且未定义的宏有什么缺点?
What are the drawbacks of such an immediately used and undefined macro?
我仍然不确定问这个问题是否是个好主意,因为它在某种程度上是一个 "coding style" 问题(而且这些问题本质上是主观的),但我希望它会是 objective那样转够了。
如果事实并非如此,我很抱歉在这里提问。
我想请教两个问题:
- 与复制粘贴我本可以忽略的实际代码相比,使用该宏是否有重大缺点? (或您能想到的任何更好的解决方案)
- 是否有更好的解决方案来实现相同的目标? (避免重复 "boilerplate metaprogramming code")
但我不问的是您个人是否喜欢这段代码。如果你愿意,我不能阻止你这么说,如果你这样做我也不介意,但我想知道它是好是坏的原因,这可能有助于将这个问题引向黑暗的一面可能基于意见的问题(假设还没有,如果是的话,我再次道歉)。
最近想写一些trait,很多都非常简单。然而,每一个都需要某种重复的元编程代码。我最终使用宏来使代码更紧凑且重复性更低,代码如下:
#include <type_traits>
//...
template<class...>
struct void_type {
typedef void type;
};
template<class... T>
using void_t = void_type<T...>::type;
#define UNARY_EXPRESSION_TYPE_TRAIT(trait_name, expression, expression_type, \
type_name) \
namespace detail_generated { \
template<class type_name, class = void> \
struct trait_name : std::false_type {}; \
\
template<class type_name> \
struct trait_name<type_name, void_t<decltype(expression)>> : \
std::integral_constant< \
bool, std::is_same<decltype(expression), expression_type>::value \
> {}; \
} \
template<class type_name> \
struct trait_name<type_name> : detail_generated::trait_name<type_name> {};
UNARY_EXPRESSION_TYPE_TRAIT(is_preincrementable,
++std::declval<Incrementable&>(),
Incrementable&, Incrementable)
UNARY_EXPRESSION_TYPE_TRAIT(is_predecrementable,
--std::declval<Decrementable&>(),
Decrementable&, Decrementable)
UNARY_EXPRESSION_TYPE_TRAIT(is_contextually_convertible_to_bool,
!std::declval<T>(), bool, T)
#undef UNARY_EXPRESSION_TYPE_TRAIT
// ...
在这种特定情况下,它避免了 "boilerplate metaprogramming constructions" 的重复,而且我没有看到很多缺点(其中大部分都通过立即使用宏及其直接取消定义而得到缓解)。
然而,我通常不太喜欢宏(我发现它们很混乱,而且通常可以替换)而且我知道它们通常被认为是坏的或 "evil"。不幸的是,即使在阅读了以下问题之后,我也找不到替换它的方法、保留它的理由或避免它的理由:
- When are C++ macros beneficial?
- Why #define is bad?
- What is an appropriate use scenario of #define in C++?
(还有那个因为它很有趣虽然不是很有用:)
- What is the worst real-world macros/pre-processor abuse you've ever come across?
我对他们的回答不满意,因为他们:
- 只在包含保护的情况下推荐宏,而没有解释为什么宏在所有其他情况下总是一个坏主意(我假设他们不鼓励使用宏,因为它们非常容易被滥用)
- 也推荐它们用于调试功能(这里不是这种情况)
- 说宏在某些情况下很棒然后 post 我实际上发现一个例子很有说服力
- 声称宏是糟糕设计的标志(这可能是真的,但我想知道问题出在哪里,以便我可以选择更好的解决方案)
我无法 objective 判断我的代码,而且我找不到该宏的任何好的替代品(除了实际执行预处理器的工作并将该宏复制粘贴到我需要的任何地方,这让我印象深刻至少同样糟糕)。也许我只是没有看到那个宏的明显和致命的缺点,或者我什至没有完全理解为什么不鼓励使用宏。
编辑:按照评论中的建议删除了某些名称中的前导下划线(此类名称已保留)
宏的问题在于它们忽略了我们在 C++ 中拥有的良好结构构造。 OTOH,这让我印象深刻,因为它很好地使用了宏:
- 您已将复杂的模板元编程的三个副本减少为一个
- 您使用了一个很好的长名称,因此它不太可能与头文件中定义的名称冲突。
- 宏只在一小段代码中定义,因此不会与该区域外的任何内容发生冲突。
我会说"go for it"。
我仍然不确定问这个问题是否是个好主意,因为它在某种程度上是一个 "coding style" 问题(而且这些问题本质上是主观的),但我希望它会是 objective那样转够了。
如果事实并非如此,我很抱歉在这里提问。
我想请教两个问题:
- 与复制粘贴我本可以忽略的实际代码相比,使用该宏是否有重大缺点? (或您能想到的任何更好的解决方案)
- 是否有更好的解决方案来实现相同的目标? (避免重复 "boilerplate metaprogramming code")
但我不问的是您个人是否喜欢这段代码。如果你愿意,我不能阻止你这么说,如果你这样做我也不介意,但我想知道它是好是坏的原因,这可能有助于将这个问题引向黑暗的一面可能基于意见的问题(假设还没有,如果是的话,我再次道歉)。
最近想写一些trait,很多都非常简单。然而,每一个都需要某种重复的元编程代码。我最终使用宏来使代码更紧凑且重复性更低,代码如下:
#include <type_traits>
//...
template<class...>
struct void_type {
typedef void type;
};
template<class... T>
using void_t = void_type<T...>::type;
#define UNARY_EXPRESSION_TYPE_TRAIT(trait_name, expression, expression_type, \
type_name) \
namespace detail_generated { \
template<class type_name, class = void> \
struct trait_name : std::false_type {}; \
\
template<class type_name> \
struct trait_name<type_name, void_t<decltype(expression)>> : \
std::integral_constant< \
bool, std::is_same<decltype(expression), expression_type>::value \
> {}; \
} \
template<class type_name> \
struct trait_name<type_name> : detail_generated::trait_name<type_name> {};
UNARY_EXPRESSION_TYPE_TRAIT(is_preincrementable,
++std::declval<Incrementable&>(),
Incrementable&, Incrementable)
UNARY_EXPRESSION_TYPE_TRAIT(is_predecrementable,
--std::declval<Decrementable&>(),
Decrementable&, Decrementable)
UNARY_EXPRESSION_TYPE_TRAIT(is_contextually_convertible_to_bool,
!std::declval<T>(), bool, T)
#undef UNARY_EXPRESSION_TYPE_TRAIT
// ...
在这种特定情况下,它避免了 "boilerplate metaprogramming constructions" 的重复,而且我没有看到很多缺点(其中大部分都通过立即使用宏及其直接取消定义而得到缓解)。
然而,我通常不太喜欢宏(我发现它们很混乱,而且通常可以替换)而且我知道它们通常被认为是坏的或 "evil"。不幸的是,即使在阅读了以下问题之后,我也找不到替换它的方法、保留它的理由或避免它的理由:
- When are C++ macros beneficial?
- Why #define is bad?
- What is an appropriate use scenario of #define in C++?
(还有那个因为它很有趣虽然不是很有用:)
- What is the worst real-world macros/pre-processor abuse you've ever come across?
我对他们的回答不满意,因为他们:
- 只在包含保护的情况下推荐宏,而没有解释为什么宏在所有其他情况下总是一个坏主意(我假设他们不鼓励使用宏,因为它们非常容易被滥用)
- 也推荐它们用于调试功能(这里不是这种情况)
- 说宏在某些情况下很棒然后 post 我实际上发现一个例子很有说服力
- 声称宏是糟糕设计的标志(这可能是真的,但我想知道问题出在哪里,以便我可以选择更好的解决方案)
我无法 objective 判断我的代码,而且我找不到该宏的任何好的替代品(除了实际执行预处理器的工作并将该宏复制粘贴到我需要的任何地方,这让我印象深刻至少同样糟糕)。也许我只是没有看到那个宏的明显和致命的缺点,或者我什至没有完全理解为什么不鼓励使用宏。
编辑:按照评论中的建议删除了某些名称中的前导下划线(此类名称已保留)
宏的问题在于它们忽略了我们在 C++ 中拥有的良好结构构造。 OTOH,这让我印象深刻,因为它很好地使用了宏:
- 您已将复杂的模板元编程的三个副本减少为一个
- 您使用了一个很好的长名称,因此它不太可能与头文件中定义的名称冲突。
- 宏只在一小段代码中定义,因此不会与该区域外的任何内容发生冲突。
我会说"go for it"。