【问题标题】:Why can't I wrap a template parameter with parentheses?为什么我不能用括号包装模板参数?
【发布时间】:2022-02-06 03:55:28
【问题描述】:

为了避免 XY,我将首先解释我的总体目标。

我试图在编译时在两个不同的通用容器之间做出选择。我提出的解决方案使用宏非常简单。为了演示,这里是 std::vectorstd::set 的外观(实际上它们是其他容器,但与问题无关)

// switch between the container types
//#define CONT_SET

#ifdef CONT_SET
#define CONTAINER(type) std::set<type>
#else
#define CONTAINER(type) std::vector<type>
#endif

int main() {
    CONTAINER(float) cont;    
}

这工作得很好。
例如,当我尝试在容器中存储更复杂的类型时,就会出现问题

CONTAINER(std::pair<int, int>) cont;

这不起作用,因为编译器将其检测为两个不同的宏参数std::pair&lt;intint&gt;
我尝试通过添加将整个类型组合在一起的括号来克服这个问题

CONTAINER((std::pair<int, int>)) cont;

然后我得到一个“模板参数 1 无效”(godbolt)

有没有办法告诉编译器整个表达式只是一个宏参数?或者括号中的模板参数是一个有效的类型?

【问题讨论】:

  • 分别是usingtypedef别名呢?可能需要template
  • 对于 XY 部分:不需要为此使用宏。您可以简单地使用类型别名。你还想问宏问题还是没有宏的替代方案?
  • 你可以简单地使用#define CONTAINER(...) std::set&lt;__VA_ARGS__&gt;
  • 为什么不直接template&lt;class T&gt; using XXX = std::conditional_t&lt;xxx, std::vector&lt;T&gt;, std::set&lt;T&gt;&gt;
  • @πάνταῥεῖ 问题是宏通过文件泄漏。我知道通常它被认为是宏的一个可怕方面,但在这种情况下它是一个优势。我正在尝试提供一个库,其中标头的使用者可以选择在整个库中全局设置或取消设置此开关,我不知道有什么方法可以让usings 像那样工作跨度>

标签: c++ templates macros preprocessor


【解决方案1】:

假设我认为 C/C++ 宏是邪恶的(在我看来,您可以使用 using 替换 Container()),您可以通过类型别名传递

using pair_i_i = std::pair<int, int>;

CONTAINER(pair_i_i) cont;

【讨论】:

    【解决方案2】:

    对于预处理器,std::pair&lt;int, int&gt; 看起来像两个模板参数,std::pair &lt; intint &gt;,因为逗号没有被保护。但通常也不允许在类型名称中使用额外的括号,因此 (std::pair&lt;int, int&gt;) 在语法上无效。

    有几种方法可以解决这个问题:

    • 如果您的类型是函数宏的最后一个参数(就像您的情况一样),您可以将其设为可变参数:
    #define CONTAINER(...) std::vector< __VA_ARGS__ >
    
    • 您可以使用不需要保护的逗号宏:
    #define COMMA ,
    
    CONTAINER(std::pair<int COMMA int>) cont;
    
    • 您可以引入不带逗号的类型别名
    using cont_value = std::pair<int, int>;
    
    CONTAINER(cont_value) cont;
    
    • 您可以用不同的方式引入括号来保护逗号(这里我在decltype(...) 中使用括号)
    #define GUARD_TYPE_NAME(...) typename decltype(std::type_identity< __VA_ARGS__ >())::type
    
    CONTAINER(GUARD_TYPE_NAME(std::pair<int, int>)) cont;
    

    【讨论】:

    • GUARD_TYPE_NAME 不妨扩展为__VA_ARGS__,调用它所需的括号就足够了。 :)
    【解决方案3】:

    我认为更简洁的解决方案是将你正在做的任何事情封装在你自己的类型中:

    template <typename type> 
    class my_container
    {
    #ifdef CONT_SET
        std::set<type> cont;
    #else
        std::vector<type> cont;
    #endif
    };
    
    int main() {
        my_container<int> a;
    }
    

    【讨论】:

      【解决方案4】:

      这个怎么样?

      // switch between the container types
      //#define CONT_SET
      
      #ifdef CONT_SET
      template <typename T>
      using container = std::set<T>;
      #else
      template <typename T>
      using container = std::vector<T>;
      #endif
      

      这减少了宏的使用,这总是好的。

      【讨论】:

      • 我认为这是最干净的答案。
      【解决方案5】:

      解决此问题的最常见方法是使用 using 别名作为提到的其他答案。

      但是,如果您想避免使用参数化宏并且还能够使您的某些 Container 变量独立于 CONT_SET,这是使用模板特化的另一种方法。

      #define CONT_SET
      
      #ifdef CONT_SET
      #define MYTYPE signed  // selects set.
      #else
      #define MYTYPE unsigned  // selects vector.
      #endif
      
      #define SET signed     // selects set.
      #define VECTOR signed  // selects vector.
      
      // General case
      template <typename T1, typename T2>
      class Container {};
      
      // Specialized when T2=unsigned, selects vector.
      template <typename T1>
      class Container<T1, unsigned> {
       public:
        std::vector<T1> c;
      };
      
      // Specialized. When T2=signed, selects set.
      template <typename T1>
      class Container<T1, signed> {
       public:
        std::set<T1> c;
      };
      
      int main() {
        // This one uses set or vector depending on CONT_SET
        Container<int, MYTYPE> my_cont;
      
        // This one uses set
        Container<int, SET> set_cont;
      
        // This one uses vector
        Container<int, VECTOR> vector_cont;
        return 0;
      }
      

      https://godbolt.org/z/q9W39s7qx

      【讨论】:

        猜你喜欢
        • 2013-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-05
        • 2019-11-16
        • 2020-06-20
        • 1970-01-01
        相关资源
        最近更新 更多