【问题标题】:Macro for (SFINAEd) template function(SFINAEd) 模板函数的宏
【发布时间】:2020-07-27 13:21:48
【问题描述】:
#define BINDINGTEMPLATE template<typename T, typename = typename std::enable_if_t < std::is_same_v<typename std::decay_t<T>, int> || std::is_same_v<typename std::decay_t<T>, std::string> || std::is_same_v<typename std::decay_t<T>, char>>>

这样的做法是不好的吗?

我在同一个类中多次使用这个函数模板。

BINDINGTEMPLATE
void myFunction(int x, int y)
{
   // do something specialised based on input template
}

例如,我需要在很多功能中使用它,比如这个:

【问题讨论】:

  • 什么是“不良做法”纯粹是基于意见。虽然,是的。你为什么要使用宏而不是类型特征?
  • 你是什么意思?我怎样才能多次重写这个模板呢?
  • 如果没有宏也可以实现,那么这是不好的方法,因为宏有很多缺点。所以请描述一下这个宏应该做什么。
  • 我在代码中添加了一个示例。 @MarekR
  • 顺便说一句,SFINAE 可以很好地在不同的替代重载之间进行选择,如果您只有一个重载,如您的示例中那样,与更简单的 static_asserts 相比,SFINAE 没有任何好处

标签: c++ templates sfinae typetraits function-templates


【解决方案1】:

是的,这是一种不好的做法。不要将宏用于无需它们即可轻松完成的事情。

你可以把那个长的 SFINAE 条件变成类似的东西

template <typename T> using foo = std::enable_if_t<...>;

然后你可以简单地写:

template <typename T, typename = foo<T>>
void myFunction(int x, int y)

或者,您可以将条件放入constexpr 函数或变量模板中,然后在每次使用时写入enable_if_t

您也可以使用概念(需要 C++20):

template <typename T>
concept foo = std::is_same_v<typename std::decay_t<T>, int> || etc;

template <foo T>
void myFunction(int x, int y)

请注意,enable_if_t 的这种使用(无论您是否使用帮助程序 using)不是很可靠,因为用户可以通过显式指定第二个模板参数来规避它。

更好的方法是:

template <typename T, std::enable_if_t<..., std::nullptr_t> = nullptr>
void myFunction(int x, int y)

除了万无一失之外,这还可以让您根据不同的特征重载函数。

这些概念也解决了这两个问题。

【讨论】:

    【解决方案2】:

    你可以通过 type-trait 实现同样的效果:

    template <typename T>
    using enable_if_int_or_string_or_char = std::enable_if_t < 
                                 std::is_same_v<typename std::decay_t<T>, int> 
                                 || std::is_same_v<typename std::decay_t<T>, std::string> 
                                 || std::is_same_v<typename std::decay_t<T>, char>>;
    

    然后您可以使用enable_if_int_or_string_or_char&lt;T&gt; 而不是宏。宏有严重的缺点,所以最好的宏是你不需要使用的那个。

    【讨论】:

    • 我正在用同样的方法写一些东西,所以 +1 来自我。
    • @MarekR 不幸的是没有。你仍然有机会写下答案:P
    • 当我们使用decay_t时,我们真的需要typename吗?不是已经在_t 别名中了吗?
    【解决方案3】:

    恕我直言,宏是邪恶的。

    所以,是的:(恕我直言)是不好的做法。

    我提出了另一种方法(只是为了好玩:其他答案显示了很好的解决方案)来避免它。

    您可以如下声明foo()(启用/禁用SFINAE功能)

    template <typename T>
    std::enable_if_t<std::is_same_v<std::decay_t<T>, int>
                  || std::is_same_v<std::decay_t<T>, std::string>
                  || std::is_same_v<std::decay_t<T>, char>> foo ();
    

    你可以使用它来编写你的最终函数

    template <typename T>
    decltype(foo<T>()) bar (int, int)
     { }
    

    以下是完整的编译示例

    #include <string>
    #include <type_traits>
    
    template <typename T>
    std::enable_if_t<std::is_same_v<std::decay_t<T>, int>
                  || std::is_same_v<std::decay_t<T>, std::string>
                  || std::is_same_v<std::decay_t<T>, char>> foo ();
    
    template <typename T>
    decltype(foo<T>()) bar (int, int)
     { }
    
    int main ()
     { 
       bar<int>(0, 0);     // compile
       // bar<long>(0, 0); // compilation error
     }
    

    【讨论】:

      【解决方案4】:

      这是一个特征检查示例,如果类型是您要查找的类型之一:

      template <typename T>
      struct is_int_string_char
      {
        static const bool value = std::is_same<std::decay<T>::type, int>
                               || std::is_same<std::decay<T>::type, std::string>
                               || std::is_same<std::decay<T>::type, char>;
      };
      

      您可以将其用作编译类型的常量is_int_string_char&lt;MyType&gt;::value。在你的情况下(如果我很好理解你的代码),你可以简化它:

      template <typename T, typename U = std::enable_if<is_int_string_char<T>>
      

      【讨论】:

        【解决方案5】:

        由于 C++ 提供了许多其他选项来解决此问题,因此不应使用宏 (IMO)。

        例如,您可以创建一个variable template,您可以在其中指定任何传递的模板TypesmyFunction 的模板类型T 相同,SFNIAE 是解决方案。

        感谢std::disjunction,通过它可以将这个想法推广到任意数量的Types,并且噪音更小!

        #include <string>
        #include <type_traits> // std::enable_if_t, std::is_same, std::disjunction_v
        
        template<typename T, typename... Types>
        constexpr bool  isAcceptableType = std::disjunction_v<std::is_same<T, Types>...>;
        
        template <typename T>
        auto myFunction(T x, T y) -> std::enable_if_t<isAcceptableType<T, char, std::string, int>>
        {
           // do something
        }
        

        See a demo

        【讨论】:

          猜你喜欢
          • 2014-03-18
          • 1970-01-01
          • 1970-01-01
          • 2011-10-18
          • 1970-01-01
          • 2016-03-26
          • 2012-08-30
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多