c++引用与const指针带来的坑

起源

  • 一个平平无奇的模板函数
  • 接收两个参数: 类型为Tconst引用,一个同样参数的回调函数
  • 直接将第一个参数传入回调
    1
    2
    3
    4
    5
    template<typename T>
    void foo(const T & data, const std::function<void(const T &)> & callback)
    {
    callback(data);
    }
  • 代码看起来没有什么问题,使用一下
    1
    2
    3
    4
    5
    6
    7
    8
    int main()
    {
    int a = 1;
    int* ptr = &a;
    foo<int> (a, [](const int & data){ std::cout << data << std::endl; });
    foo<int*>(ptr, [](const int* & data){ std::cout << *data << std::endl; });
    return 0;
    }
  • 使用g++编译,第一个Tintfoo调用没有问题,但Tint*编译器会报错:找不到匹配的函数
  • 初一看就很奇怪了

const引用与const指针

  • 仔细分析下所谓const引用就是意味着变量不能被修改为其他值
  • 那么const引用的指针也即指针不能修改为其他值,即不能指向别的区域
  • 这里就和void* const这样的指针等价了
  • 所以这里编译器应该是针对指针进行了语义转换(或者说内部引用的实现就是通过指针完成的)
  • 这样就可以理解了,模板参数int*会被当做int*const,也就导致我们参数不一致编译失败
  • 如此,修改起来就简单了,把int*改为int*const即可
    1
    2
    3
    4
    5
    6
    7
    8
    int main()
    {
    int a = 1;
    int* ptr = &a;
    foo<int> (a, [](const int & data){ std::cout << data << std::endl; });
    foo<int*const>(ptr, [](const int* const & data){ std::cout << *data << std::endl; });
    return 0;
    }

没有const的引用就是普通的指针

  • 进一步验证,把fooT的引用不使用const
  • 此时模板参数就可以为int*
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    template<typename T>
    void foo(const T & data, const std::function<void(const T &)> & cb)
    {
    cb(data);
    }

    template<typename T>
    void foo2(T & data, const std::function<void(T &)> & cb)
    {
    cb(data);
    }

    int main()
    {
    int a = 1;
    int* ptr = &a;
    foo<int>(a, [](const int & data){ std::cout << data << std::endl; });
    foo<int*const>(ptr, [](const int* const & data){ std::cout << *data << std::endl; });
    foo2<int*>(ptr, [](int* & data){ std::cout << *data << std::endl; });

    return 0;
    }
    以上