为什么我不能用括号括起模板参数?

Why can't I wrap a template parameter with parentheses?

为了避免 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<intint>.
我尝试通过添加将整个类型组合在一起的括号来克服这个问题

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

但后来我得到一个 'template argument 1 is invalid' (godbolt)

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

前提是我认为 C/C++ 宏是邪恶的(在我看来你可以用 using 代替 Container())你可以通过类型别名

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

CONTAINER(pair_i_i) cont;

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

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

  • 如果您的类型是函数宏的最后一个参数(就像您的情况一样),您可以使其可变:
#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;

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

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;
}

解决此问题的最常见方法是使用 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

这个怎么样?

// 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

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