成员声明中的模板 class 类型别名替换失败

Template class type alias failing substitution in member declaration

假设您有一个像这样的模板 class

template <typename type>
class Object {
  using length_t = unsigned int;
  
  template <length_t length>
  void put(type (&)[length]);
};

并且您在其中声明了一个 put(...) 方法。 您如何在 class 之外声明 put(...) 方法?

  1. 这是有人可能会采用的一种方法:

    /* ERROR: Doesn't match any declarations(?) */
    template <typename type>
    template <typename Object<type>::length_t length>
    void Object<type>::put(type (&)[length]) {}
    

    但这会导致一个奇怪的错误

    error: no declaration matches 'void Object<type>::put(type (&)[length])'
    
    note: candidate is: 
      template <class type>
      template <unsigned int length>
      void Object<type>::put(type (&)[length])
    
  2. 这是另一种声明 put(...) 方法使其有效的方法:

    /* SUCCESS: But `length_t` alias isn't used */
    template <typename type>
    template <unsigned int length>
    void Object<type>::put(type (&)[length]) {}
    

    但未使用 class 中定义的 length_t 类型别名。

如何让第一个定义发挥作用,从而使 class 的特性(如类型别名)的使用在其声明和定义中保持一致,或者第二个定义是唯一的解决方案在吗?

How does one get the first definition to work so as to keep the use of the class's features (like type aliases) consistent across its declaration & definitions,

我不得不承认我不明白这个错误,我不知道如何仅通过更改定义来修复它。错误信息相当混乱(你应该把它包括在问题中)。

... or is the second definition the only solution here?

不,不是。如果您不介意 length_t 不是会员,那么这可能会为您指明正确的方向:

template <template<typename> typename T>
struct length { using type = int; };

template <template<typename> typename T>
using length_t = typename length<T>::type;


template <typename> struct Object;
template <> struct length<Object> { using type = unsigned int; };

template <typename type>
class Object {
  //using length_t = unsigned int;
  
  template <length_t<Object> length>
  void put(type (&)[length]);
};

template <typename type>
template <length_t<Object> length>
void Object<type>::put(type (&)[length]) {}

length 是一个“模板特征”(不确定这个术语是否真的存在)。而不是将 length_t 作为 Object 的成员,您需要为 length<Object> 提供专门化(并且需要 Object 的前向声明)。 int 基本情况仅用于说明。如果你愿意,你仍然可以添加一个成员到 Object 到别名 length_t<Object>.

Live Demo

我认为这是一个编译器错误,或者更进一步,是标准中的一个缺陷。

你的代码实际上没有问题,并且被MSVC接受。如果将定义放在 class 中,编译器将不会认为它是 ill-formed.

我发了一个 that is similar to this. and I get the result that, CWG2,不知道什么时候发的老问题,还在起草,意思是匹配规则out-of-definition 甚至 未指定 。这些奇怪的不匹配是因为编译器的不同实现。

然后,如何避免这个问题,首先你可以把定义放在class里面。如果它依赖于 class 定义后面定义的东西并且不能在里面定义,你可以:

  1. 让它独立:让using length_t = unsigned int;在外面。
  2. 声明时使其可推导:编译器可能不知道typename Object<type>::length_tlength_t(在class内)是否是同一类型,尽管 typename Object<type>::length_t 不需要被推导。因为在声明时,编译器无法确保是否指定了 Object<type> 并使 length_t 不匹配,在我看来。正如@idclev 463035818 所说,template<...> using length_t = unsigned int; 将使编译器更容易匹配此定义。