【问题标题】:Template class type alias failing substitution in member declaration成员声明中的模板类类型别名替换失败
【发布时间】:2020-10-29 20:25:50
【问题描述】:

假设您有一个这样的模板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 的特性(如类型别名)的使用一致,或者第二个定义是这里唯一的解决方案?

【问题讨论】:

  • 因此,在现代 C++ 代码中可能存在一些不常见或不被接受的代码示例的设计/样式选择,但我想声明提出的问题仍然存在,尽管对如何代码看起来或它的功能是什么。
  • 没关系。不过,您可以将该免责声明添加到问题本身,而不是作为评论。
  • lenght_t 作为类型特征而不是成员别名的选项吗?我想不会,你不想改变类的声明
  • 人们如何回答或查看它与他们的回答方式有关;)
  • 我知道,这就是我在问题中读到的内容。但是将lenght_t 不作为成员而是作为类型特征是否可以接受?而不是Object&lt;T&gt;::lenght_t,它将类似于length_t&lt;T&gt;length_t&lt;Object&lt;T&gt;&gt;

标签: c++ class templates alias declaration


【解决方案1】:

如何让第一个定义发挥作用,以便在其声明和定义中保持对类特性(如类型别名)的使用一致,

我不得不承认我不理解错误,也不知道如何仅通过更改定义来修复它。错误消息相当混乱(您应该将其包含在问题中)。

...或者第二个定义是这里唯一的解决方案?

不,不是。如果您对 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&lt;Object&gt; 提供一个特化,而不是让length_t 成为Object 的成员(这需要Object 的前向声明)。 int 基本案例仅用于说明。如果您愿意,您仍然可以将成员添加到 Object 到别名 length_t&lt;Object&gt;

Live Demo

【讨论】:

  • 非常感谢您到目前为止的时间,但有几件事:“错误消息相当混乱(您应该将其包含在问题中)”,我相信已经按照建议完成了(除非存在误解),因为尝试第一次尝试的(实际)编译器错误与问题中表达的错误完全相同(考虑到&lt;template-parameter-1-1&gt; 等一些迂腐的细节已被编辑)。
  • 您的解决方案做得非常好(并受到赞赏),但第三次尝试达到同样的效果,即不让 length_t 别名成为 Object 类的成员:godbolt.org/z/ba7Par
  • @Lapys 再想一想,我认为您无需更改定义即可使用该特征。您的解决方案没有实际意义,因为那样问题就不存在了。问题的前提是legnth_t依赖Object否则没有问题
  • @Lapys 完整的错误信息不止于此。我的编译器对候选人给出了很好的提示(这就是有趣和令人困惑的部分)godbolt.org/z/59PPKE
  • @Lapys 不同的话:请在问题中包含完整的编译器错误消息;)
【解决方案2】:

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

您的代码实际上没有问题,并且被 MSVC 接受。如果你把定义放在类中,没有编译器会认为它格式错误。

我已经发布了一个与此类似的question。我得到的结果是,CWG2,这个什么时候发布的没人知道的古老问题,仍然是草拟,这意味着匹配规则不规则甚至是未指定。这些奇怪的不匹配是由于编译器的不同实现造成的。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-31
    • 1970-01-01
    • 2011-01-30
    • 1970-01-01
    • 1970-01-01
    • 2018-05-20
    • 2016-01-06
    • 2014-04-22
    相关资源
    最近更新 更多