为什么隐式转换不适用于模板化函数参数?

Why is implicit conversion not applied to templated function parameter?

我遇到了一些模板问题,我已将其缩小为以下示例 (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); 

}

在那个例子中:

该示例的编译失败 (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);
      |                        ^

根本错误似乎是:

types ‘const T’ and ‘int’ have incompatible cv-qualifiers

我明白字面上的意思,但我不明白为什么会这样。我的期望是 item<int> :: operator item<const int> () const 转换运算符将在调用 butNotWhenTemplated(i) 时应用,就像在调用 conversionToConstRefWorks(i) 时应用的一样,并且 int 将为 [=22= 选择].

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

我的另一个问题是:由于超出此 post 范围的原因,butNotWhenTemplated 必须是模板并且必须为所有 item 参数指定 <const T> ,并且我在调用它时无法显式指定模板参数。有没有办法在这些限制条件下进行这项工作?

Here it is on ideone(海湾合作委员会 8.3)。

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

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

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later. However, if deduction succeeds for all parameters that participate in template argument deduction, and all template arguments that aren't deduced are explicitly specified or defaulted, then the remaining function parameters are compared with the corresponding function arguments.

试试这个重载:

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

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

附录:

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