【问题标题】:Deducing class template arguments with a constructor that uses a member type alias使用使用成员类型别名的构造函数推导出类模板参数
【发布时间】:2017-04-20 21:32:33
【问题描述】:

所以显然这应该有效:

template<class T>
struct C {
   using value_type = T;
   C(value_type);
};

C c(1); // C<int>

就是这样(参见[over.match.class.deduct]/3 中的B 示例):

template<class T>
struct D {
   template<class> using meow_t = T;

   template<class U>
   D(U, meow_t<U>);
};

D d(1, 'c'); // D<char>

请注意,由于参数是非推导上下文,因此看似等效的显式指南将不起作用:

template<class T>
C(typename C<T>::value_type) -> C<T>;

虽然希望至少第一个 sn-p 有效,但我还没有找到真正使其在当前有效的措辞工作草案。有谁知道它在哪里?

【问题讨论】:

  • 无关:d(1, 'c') 不应该吐出类似于deduced conflicting types for parameter 'T' ('int' and 'char') 的错误吗?或者这里meow_t&lt;U&gt; 是在非推断上下文中?但如果是,那么代码中的注释是错误的,// D&lt;char&gt; 应该是// D&lt;int&gt;
  • 没有。 meow_t&lt;U&gt;T
  • 对,我就是这么想的。但是在d(1, 'c') 中,你有一个int 和一个char。为什么类型扣除会在这里起作用,因为(afaik)没有执行转换?例如,这个 C++1z 之前的示例无法编译:coliru.stacked-crooked.com/a/f57c46020edd62fd
  • 再看一遍,是T,不是U
  • 哦,是的,谢谢,我的错!

标签: c++ language-lawyer c++17


【解决方案1】:

这不是严格意义上的答案,因为我认为这样的措辞实际上并不存在。这更像是与问题相关的信息的拼凑。


这是Core Issue 2。 Oulu 和 Issaquah 关于此功能的讨论清楚地表明,意图是查看 typedef 是有效的,但没有添加任何措辞来表明它应该如何工作 - 它只是。 .. 是。原样的措辞表明以下扣除指南:

template<class T>
struct C {
   using value_type = T;
   C(value_type);
};

应该是:

template <class T> C<T> foo(typename C<T>::value_type );

这将是一个非演绎的上下文并且失败,但[thread.lock.guard] 没有针对这种情况的明确演绎指南。

[over.match.best] 中的示例显然是为了表明 typedef 应该起作用,尽管该示例中的示例实际上都没有使用 #1 作为推导指南:

template <class T> struct A {
  using value_type = T;
  A(value_type);    // #1
  A(const A&);      // #2
  A(T, T, int);     // #3
  template<class U>
    A(int, T, U);   // #4
  // #5 is the copy deduction candidate, A(A)
};

A x(1, 2, 3);       // uses #3, generated from a non-template constructor

template <class T>
A(T) -> A<T>;       // #6, less specialized than #5

A a(42);            // uses #6 to deduce A<int> and #1 to initialize
A b = a;            // uses #5 to deduce A<int> and #2 to initialize

template <class T>
A(A<T>) -> A<A<T>>; // #7, as specialized as #5

A b2 = a;           // uses #7 to deduce A<A<int>> and #1 to initialize

【讨论】:

  • 不过,它并不完全是 CWG2。推导指南不是成员,由它们合成的函数/函数模板不是原始类模板的成员。
  • @T.C.是的,不完全是,但它在同一个轨道上。我怀疑这只会增加标准中未充分说明的与模板相关的问题。
  • @T.C.想在标准讨论中加入一些东西吗?
  • 我相信我们在 Issaquah 的讨论中描述的方式是“这里发生了剧烈的挥手”。预期的规则是 CWG 2 的规则:如果您可以通过仅使用模板中的信息来确定类型是什么,那么您可以反对该类型。 (换句话说,如果使用可推导类型重新声明构造函数是有效的,那么您可以针对该类型进行推导。但这需要您回到 CWG 2 来确定重新声明何时有效。)
【解决方案2】:

我认为仔细阅读可以清楚地说明这一点:

函数参数的类型是构造函数的类型。

注意C&lt;T&gt;::value_type 不是一个类型,它是一个typedef-name。由于代入,构造函数参数的类型为T,等价的推导其实是

template <class T> C(T) -> C<T>;

在您的示例中,使用 C&lt;T&gt;::value_type 看起来好像存在与非推导上下文有关的问题,但实际上并不存在该问题,因为不涉及对推导函数参数的查找。

【讨论】:

  • 同样的论点适用于T:“它是一个typedef-name”(参见[temp.param]/3);它“不是一种类型”。我看不出您如何根据该论点在此处得出原则性的区别。
  • 我明白你的意思,但这是我能用代码得到的最接近的。使用value_type 不是很接近,它会导致问题看起来像是一个非推断的上下文,这实际上并不相关,因为不涉及查找。
  • 有问题的构造函数与其他情况不同,它是类 template 的成员,而不是类(例如, 类模板专业化)。它的参数类型 T,因为尚未提供T 的值。是的,这是一个丑陋的语言黑客。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-21
  • 1970-01-01
相关资源
最近更新 更多