为什么模板功能参数类型为T&被绑定到const左值而不是右值?

时间:2017-11-13 21:20:32

标签: c++ templates

考虑:

template <typename T> void f(T&);

const int b = 2;
f(b);      // ok
f(2);      // error, can not bind rvalue to lvalue reference

为什么允许f(const int)?逻辑似乎表明如果程序员没有明确地将模板参数定义为const T&,他/她想要修改binded-to变量。

因此,在这种情况下,问题是,为什么模板实例化在没有明确要求的情况下为自己提供了实例化自由的自由?

即使有一个允许模板实例化用consts实例化的理由,那么为什么在这种情况下会绑定到禁止的rvalues呢?你可以将rvalues绑定到const左值引用。在这种情况下,模板将被实例化为f<const int>,并且f(2)将被允许。

我想知道这些决定背后的原因,而不是对标准的引用。

2 个答案:

答案 0 :(得分:4)

  

因此,在这种情况下,问题是,为什么模板实例化在没有明确要求的情况下为自己提供了实例化自由的自由?

很简单,f的实现中没有任何内容阻止使用const int实例化模板。请尝试以下方法,然后您会发现问题。

template <typename T> void f(T& v) { v = 3; };
const int b = 2;
f(b);      // error: assignment of read-only reference (within the template instance)
int c = 2;
f<const int>(c); // error

b为const int这一事实意味着模板被实例化为<const int>,这是不可能的,因为v已被修改。同样禁止显式实例化为<const int>

  

为什么推断为const int?它应该被推断为int

当没有明确指定实例化模板的类型时,它是根据传递给函数const int的类型推导出来的。期望它推断出不同的类型是不切实际的(如果不是完全荒谬的话)。

另请注意,如果确实将类型推断为int,那么将const int传递给函数时会出错。可以通过将模板显式实例化为<int>来证明。

template <typename T> void f(T&) {};
const int b = 2;
f<int>(b); //error: no matching function for call to 'f<int>(const int&)
  

为什么,在这种情况下,会绑定到禁止的rvalues?

编译器需要隐式假设一个适当的类型用于实例化模板 然后 执行从rvalue到const lvalue引用的隐式转换。 (似乎是对我的期待。)

请注意,如果您将模板显式实例化为<const int>,则将rvalue绑定到const lvalue引用是有效的,因为可以进行转换。

template <typename T> void f(T&);
f<const int>(2);      // OK

答案 1 :(得分:4)

  

为什么允许使用f(const int)?

您可以通过T替换模板const int,将T&转换为const int&

  

即使有一个允许模板实例化用consts实例化的理由,那么为什么在这种情况下会绑定到禁止的rvalues呢?

没有T(在T&中)(确切)匹配int&&进行扣除。

f<const int>(42)是允许的,但没有发生扣减。

  

我想知道这些决定背后的原因,而不是对标准的引用。

那么为什么在模板中允许cv替换呢? 我想说它使通用编程更容易 否则,您必须为constvolatile的每个组合提供重载。

此处,如果您想将T限制为非const,则可以使用具有特征的SFINAE:

template <typename T> std::enable_if_t<!std::is_const<T>::value> f(T&);