【问题标题】:is_const doesn't work as expected for referenceis_const 不能按预期工作以供参考
【发布时间】:2016-09-09 05:17:39
【问题描述】:

我写了一个测试程序:

#include <iostream> 
#include <type_traits> 
using namespace std; 
template<class T> 
void f(T&& t) 
{ 
     cout<<is_const<T>()<<endl; 
     //++t; 
} 
int main() { 
     const int i=0; 
     f(i); 
     return 0; 
} 

它输出“0”,表明T 不是常量!这很奇怪。然后我修改了f

template<class T> 
void f(T&& t) 
{ 
     cout<<is_const<T>()<<endl; 
     ++t; 
} 

然后是编译器错误,说我们正在修改只读t。 那么t 是否可以修改?我的程序中是否存在任何错误假设?

【问题讨论】:

  • @101010 用法正确。 is_const 有一个合适的转换运算符。
  • @juanchopanza 感谢我不知道的信息。
  • @juanchopanza cout&lt;&lt;is_const&lt;T&gt;()&lt;&lt;endl; 中的什么会强制它布尔值?
  • 试试is_const&lt;std::remove_reference&lt;T&gt;&gt;::value
  • @xaxxon 好吧,没有ostream&amp; operator&lt;&lt;(ostream&amp;, is_const&lt;T&gt;),所以没什么可挑选的。

标签: c++ c++11 templates constants


【解决方案1】:

t 是否可修改取决于T 的类型,该类型是根据传入的变量类型推导出来的。在这种情况下,您传入的是const int,因此t 是类型const int &amp; 因为您接受它作为转发参考。

至于为什么 is_const 返回 false,那是因为 T 是引用类型,而引用永远不是 const。

【讨论】:

    【解决方案2】:

    std::is_const:

    如果 T 是 const 限定类型(即 const 或 const volatile),则提供等于 true 的成员常量值。对于任何其他类型,值为 false。

    t 被声明为forwarding references。所以对于你的代码,T 将被推导出为const int&amp;,这是一个参考。引用不能是 const-qualified,它本身不会是 const。准确地说,没有 const 引用(即int&amp; const),因为引用无法再次反弹。 const int&amp; 是对const int 的引用;并注意 t 因此不可修改。

    来自标准,$8.3.2/1 References [dcl.ref]

    Cv 限定的引用格式不正确,除非 cv 限定符 通过使用 typedef-name ([dcl.typedef], [temp.param]) 或 decltype-specifier ([dcl.type.simple]),在这种情况下 cv 限定符被忽略。

    来自cppreference的更多示例:

    std::cout << std::is_const<int>::value << '\n'; // false
    std::cout << std::is_const<const int>::value  << '\n'; // true
    std::cout << std::is_const<const int*>::value  << '\n'; // false
    std::cout << std::is_const<int* const>::value  << '\n'; // true
    std::cout << std::is_const<const int&>::value  << '\n'; // false
    

    【讨论】:

      【解决方案3】:

      您的模板函数(即f)将forwarding reference(又名通用参考)作为参数。判断T的扣减规则为reference collapsing rules。这些规则总结如下:

      1. T&amp; &amp; 变为 T&amp;
      2. T&amp; &amp;&amp; 变为 T&amp;
      3. T&amp;&amp; &amp; 变为 T&amp;
      4. T&amp;&amp; &amp;&amp; 变为 T&amp;&amp;

      现在,根据引用折叠规则,当您将f作为参数提供给int const i时,T将被扣除为int const&amp;

      根据 C++ 标准表 52 如果Tconst 合格,is_const 将评估为true

      此外,在 C++ 标准 §20.13.4.3/p5 类型属性 [meta.unary.prop] 中有以下示例说明 is_const 类型特征如何工作:

      [示例:

      is_const<const volatile int>::value // true
      is_const<const int*>::value // false
      is_const<const int&>::value // false
      is_const<int[3]>::value // false
      is_const<const int[3]>::value // true
      

      —结束示例]

      正如您在第三行看到的那样,我们的案例 is_const 的计算结果为 false。为什么?因为作为模板参数传入is_const的类型是引用类型。现在,引用本质上是const,因为您无法更改它们所指的内容,但它们不是const 限定的。因此,具有引用类型的is_const 将评估为false

      【讨论】:

        【解决方案4】:

        顶级答案是 references 不是 cv 限定的。只有它们引用的类型才能被 cv 限定。

        但这是const这个词的位置很重要的时候之一。当你打电话时:

        template<class T> 
        void f(T&& t) 
        

        对于const int 类型的左值,T 被推导出来是什么?你可能会说const int&amp;(这是正确的),但看起来那个类型是const (int&amp;)。因此可能会混淆 Tconst

        但是,如果您说它推断为int const&amp;(与以前的类型相同),那么这里就没有可能的混淆,并且为什么std::is_const&lt;int const&amp;&gt;std::false_type 可能就不那么令人惊讶了。

        【讨论】:

          【解决方案5】:

          可能的解决方案是在重载中拦截 const 类型:

          template<class T> 
          void f(T&& t) 
          { 
               ++t; 
          } 
          
          template<class T> 
          void f(const T& t) 
          { 
               std::cout << "const here" << std::endl;
          } 
          

          然后对 const 对象的引用将由第二个函数处理。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-01-21
            • 2016-05-03
            • 2015-06-26
            • 2013-05-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多