【问题标题】:Why doesn't my templated function promote 'int' to 'T', where 'T' = 'double'?为什么我的模板函数不将'int'提升为'T',其中'T' = 'double'?
【发布时间】:2016-01-01 20:56:26
【问题描述】:

我有一个以typename T 为模板的课程。它包含一个函数,

template <typename T, size_t a>
myClass<T,a> operator+(myClass<T,a> lhs, const T& rhs) {
    return lhs += rhs;
}

myClass<T,a> myClass<T,a>::operator+=(const T& rhs) {
    // Do addition, depends on 'a'.
    return *this;
}

例如,当我调用它时

myClass<double, 2> myObj_double_2(constructor args);
myObj_double_2 = myObj_double_2 + 5.2;

我没问题。

如果我打电话

myObj_double_2 = myObj_double_2 + 5;

然后编译器给我一条消息,例如 - No match for 'operator+' (operand types are 'myClass&lt;double, 2ul&gt;' and 'int'). Candidates are ... note: deduced conflicting types for parameter 'const T' ('double' and 'int')

我能否以某种方式编写代码以允许传递转换为 T 的其他类型(例如,因为 double(5) 是有效的构造函数调用)?

【问题讨论】:

  • 试试myObj_double_2 = myObj_double_2 + 5.0;
  • 是的 - 这将毫无困难地工作,但它并不能解决我所问的问题(尽管它是简单而明显的解决方案)
  • 不存在int->双“促销”。
  • @LightnessRacesinOrbit:确实如此。例如,据我所知,声明void foo(double); 然后调用foo(3) 是完全合法的,并且与foo((double)3)foo(3.0) 做同样的事情。所以这里的问题更加微妙。
  • @NateEldredge:这样的隐式转换当然存在,但那不是“促销”,不是吗?

标签: c++ templates c++11 c++14 type-promotion


【解决方案1】:

当您使用模板参数推导时,一个模板参数的所有推导必须具有相同的结果。

在您的情况下,T 的两个扣除产生doubleint,它们不相同,因此扣除失败。

你可以做的只是使用一个函数参数进行模板参数推导,而另一个不推导

template <typename T, std::size_t A>
void foo(myClass<T, A> arg1, typename std::common_type<T>::type arg2);
//                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

注意std::common_type&lt;T&gt;::type本质上只是T,但是因为arg2的类型现在是一个依赖类型(它的名字出现在::的右边),所以它没有被推导出来。因此,只有第一个参数参与推演并明确产生T = double,然后第二个函数参数只有double类型,并进行通常的转换。

根据经验,模板参数推导不会跨越::

【讨论】:

  • 这很完美,但我认为您的意思是typename std::common_type&lt;T&gt;::type。您的解决方案还允许添加 const&amp;,这很棒
  • common_type 衰减类型;在这里可能不相关,但要记住。
  • 也可以在myClass 中为T(即value_type)添加一个typedef 并使用template &lt;typename T1, size_t a&gt; myClass&lt;T1,a&gt; operator+(myClass&lt;T1,a&gt; lhs, typename myClass&lt;T1,a&gt;::value_type const &amp; rhs);
  • @LightnessRacesinOrbit:是的。我们可能应该有一个std::nondeduced&lt;T&gt;::type,不是因为它比std::common_type&lt;T&gt;::type 好,而是因为它的意图很明显。
  • @MSalters: std::kill_deduceacity_t&lt;T&gt; :-)
【解决方案2】:

重载解析的编译器无法找到operator+ 的正确候选者,因为T 已被扣除为double 并且文字5 是一个整数。解决方案:

template <typename T1, typename T2, size_t a>
myClass<T1,a> operator+(myClass<T1,a> lhs, const T2& rhs) {
    return lhs += T1(rhs);
}

【讨论】:

  • T1T2 相等的情况下,这可能会导致不必要的复制 - 按值获取参数允许移动(并且可能会更快在intdouble 的情况下?)
  • @chrisb2244 你可以部分专攻这个案例T1 == T2
  • 构造函数风格的转换是危险的,如果你只想允许隐式转换,那么初始化一个 T1 类型的局部变量。
【解决方案3】:

您遇到了模板类型推导的问题。

在推导T 的值时,两个参数都被赋予“同等地位”,在这种情况下,两个参数不一致——一个说T 应该是int,另一个说T 应该是@ 987654325@.

解决此问题的正确方法是使用 Koenig 运算符。

+=+friends 设为你的类并实现内联:

template<class T, size_t a>
class myClass {
  // etc
public:
  friend myClass operator+(myClass lhs, const T& rhs) {
    lhs += rhs;
    return std::move(lhs);
  }
  friend myClass& operator+=(myClass& lhs, const T& rhs) {
    // do addition, depends on `a`
    return *this;
  }
};

这种技术做了一些奇怪的事情。它根据类的模板类型创建非template 运算符。然后在您调用 ++= 时通过 ADL(Koenig 查找)找到这些。

每个模板实例化都会获得这些运算符之一,但它们不是模板运算符,因此不会推导出 const T&amp;,并且转换会按预期进行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-21
    • 2010-11-27
    • 2022-12-21
    • 1970-01-01
    • 2018-03-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多