【问题标题】:Why is implicit conversion not applied to templated function parameter?为什么隐式转换不适用于模板化函数参数?
【发布时间】:2021-03-31 17:40:30
【问题描述】:

我遇到了一些模板问题,我已将其范围缩小到以下示例 (C++17):

template <typename T> struct item {
  operator item<const T> () const { return item<const T>(); }
};

void conversionToConstRefWorks (const item<const int> &) { }

template <typename T> 
void butNotWhenTemplated (const item<const T> &) { }

int main () {

  item<int> i;
  item<const int> ci;

  // these all compile fine:
  conversionToConstRefWorks(ci);
  conversionToConstRefWorks(i);
  butNotWhenTemplated(ci);

  // but this one fails:
  butNotWhenTemplated(i); 

}

在那个例子中:

  • item&lt;T&gt; 具有到 item&lt;const T&gt; 的隐式转换运算符,并且
  • 转换似乎在 conversionToConstRefWorks() 中有效,但是
  • butNotWhenTemplated() 中似乎错过了转换,其中 item&lt;const int&gt; 可以很好地传递,但传递 item&lt;int&gt; 无法编译。

该示例的编译失败 (GCC 9.3):

g++ --std=c++17 -W -Wall -pedantic -Wno-unused-variable    const_interop.cpp   -o const_interop
const_interop.cpp: In function ‘int main()’:
const_interop.cpp:54:24: error: no matching function for call to ‘butNotWhenTemplated(item<int>&)’
   54 |   butNotWhenTemplated(i);
      |                        ^
const_interop.cpp:40:6: note: candidate: ‘template<class T> void butNotWhenTemplated(const item<const T>&)’
   40 | void butNotWhenTemplated (const item<const T> &) {
      |      ^~~~~~~~~~~~~~~~~~~
const_interop.cpp:40:6: note:   template argument deduction/substitution failed:
const_interop.cpp:54:24: note:   types ‘const T’ and ‘int’ have incompatible cv-qualifiers
   54 |   butNotWhenTemplated(i);
      |                        ^

根本错误似乎是:

类型“const T”和“int”具有不兼容的 cv 限定符

我理解字面意义上的意思,但我不明白为什么会这样。我的期望是在调用butNotWhenTemplated(i) 时应用item&lt;int&gt; :: operator item&lt;const int&gt; () const 转换运算符,就像在调用conversionToConstRefWorks(i) 时应用它一样,并且int 将被选择用于T

我的主要问题是:为什么不编译?

我的另一个问题是:由于本文范围之外的原因,butNotWhenTemplated 必须是一个模板,并且必须为所有item 参数指定&lt;const T&gt;,并且我不能在调用时显式指定模板参数它。有没有办法在这些限制条件下完成这项工作?

Here it is on ideone (GCC 8.3)。

【问题讨论】:

  • 但是以标准的名义,为什么this 有效? (而不是Item&lt;T&gt; 我使用T*
  • @RinKaenbyou 除非我误读/误解,否则我认为这是因为 int *int const * const &amp; 转换不是在模板参数内完成的(与 Item&lt;T&gt;Item&lt;const T&gt; 不同-- 你的在精神上更类似于Item&lt;T&gt;const Item&lt;T&gt;)。例如。 Tconst T 是同一类型,仅在 cv 限定符上有所不同,而 Item&lt;T&gt;Item&lt;const T&gt; 是完全不同的类型,因为模板参数不同,然后关于推导规则的一些事情就可以了。可能是。奥托我不知道我在说什么。

标签: c++ templates gcc c++17 implicit-conversion


【解决方案1】:
item<int> i;
template <typename T> void butNotWhenTemplated (const item<const T> &) { }
butNotWhenTemplated(i); 

根据template argument substitution 规则,找不到Titem&lt;const T&gt; 匹配item&lt;int&gt;。在考虑任何转换(内置或用户定义)之前,这会以硬错误方式失败。

类型推导不考虑隐式转换(除了上面列出的类型调整):这是重载解析的工作,稍后会发生。但是,如果参与模板实参推导的所有参数推导成功,并且所有未推导的模板实参都被显式指定或默认,则剩余的函数参数将与相应的函数实参进行比较。

【讨论】:

  • 嗯……那很臭。太糟糕了,在演绎发生之前无法告诉编译器哪些转换是可能的。 :(
  • @JasonC 你总是可以使用技巧:coliru.stacked-crooked.com/a/b34585169034b311
  • 我并不讨厌这个主意。我很快就会玩它。
【解决方案2】:

试试这个重载:

template <typename T>
void butNotWhenTemplated(const item<const T>&) { }

template <typename T>
void butNotWhenTemplated(const item<T>& x) {
    butNotWhenTemplated<const T>(x);
}

附录:

您试图通过引用传递给 const,但隐式转换会创建对象的副本,即使在非模板情况下也是如此。您可能想在这里重新考虑您的设计。

【讨论】:

  • 我的真实代码有多个参数,const-ness 可能会有所不同,但必须始终具有相同的基本类型;我可以用ideone.com/8A1KC9 做类似你的想法的事情。
  • 它没有给我递归(使用 Visual C++)。 AFAIK 重载决议应该更喜欢 const 限定版本。也许你的代码有什么不同?
  • 我想我确实在我的代码中做了一些不同的事情;因为我刚刚又试了一次你的,很好。现在试图记住我做了什么来进行递归,这让我大吃一惊。
  • ideone.com/YbaKSz 确实卡住了,这与我在第一条评论中的链接相同,但函数名称相同。实在想不通为什么。我不知道,我的头很痛,但无论如何我喜欢这种方法。
  • test(i, c, c) 和 test(c, c, c) 工作,所以我认为编译器认为你只是想指定第一种类型,这样做是有效的: ideone.com/Yg1YC8
猜你喜欢
  • 2016-01-23
  • 1970-01-01
  • 1970-01-01
  • 2022-10-02
  • 2019-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多