C++函数头匹配: how does matching work when const and templates are both involved?

标签 c++ templates constants shared-ptr

我有一个我想调用的模板化函数。这是标题(的精简版本):

template <typename Item>
void print (shared_ptr<const MyContainer<Item>> stuff, ostream& out)

我试图用这样的行来调用:

print (make_shared<MyContainer<int>>(42), cerr);

但是编译器提示没有匹配。让我困惑的是 const 不匹配不是问题,因为如果我重新声明我的函数以省略模板,它就可以工作:

void print (shared_ptr<const MyContainer<int>> stuff, ostream& out)  //matches!

另一方面,如果我省略常量,模板化版本确实可以工作:

template <typename Item>
void print (shared_ptr<MyContainer<Item>> stuff, ostream& out)  //also matches!

但是我应该能够在常量事物上编写一个函数,并向其传递一个非常量值(然后该函数将不会修改该值),对吧?事实上,如果我回到非托管指针,相应的旧方法来编写 header 将是

template <typename Item>
void print (const MyContainer<Item>* stuff, ostream& out)

然后确实调用了

print (new MyContainer<int>(42), cerr);  //yet another match!

再一次就好了。

那么,这个由 shared_ptr、模板和 const 组成的特定组合到底是什么原因导致编译器无法找到匹配的函数呢? (运行 g++ 8.2.1 和 clang++ 7.0.1 似乎会产生相同的结果。)

最佳答案

关于指针的常量性,std::shared_ptr行为与原始指针略有不同。

一个std::shared_ptr<T>std::shared_ptr<const T> 不同。它甚至不兼容允许隐式转换。 (Daniels answer 中的错误消息从字面上说明了这一点。)

它不起作用的原因与以下(计数器)示例相同:

template <typename T>
struct ContainerT {
  T a;
  ContainerT(T a): a(a) { }
  ContainerT(const ContainerT&) = default;
  ContainerT& operator=(const ContainerT&) = default;
};

int main()
{
  ContainerT<int> a(42);
  ContainerT<const int> b(a);
  return 0;
}

输出:

g++ (GCC) 8.2.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

main.cpp: In function 'int main()':
main.cpp:15:28: error: no matching function for call to 'ContainerT<const int>::ContainerT(ContainerT<int>&)'
   ContainerT<const int> b(a);
                            ^
main.cpp:8:3: note: candidate: 'constexpr ContainerT<T>::ContainerT(const ContainerT<T>&) [with T = const int]'
   ContainerT(const ContainerT&) = default;
   ^~~~~~~~~~
main.cpp:8:3: note:   no known conversion for argument 1 from 'ContainerT<int>' to 'const ContainerT<const int>&'
main.cpp:7:3: note: candidate: 'ContainerT<T>::ContainerT(T) [with T = const int]'
   ContainerT(T a): a(a) { }
   ^~~~~~~~~~
main.cpp:7:3: note:   no known conversion for argument 1 from 'ContainerT<int>' to 'int'

Live Demo on coliru


std::shared_ptr为例,有一种方法可以规避这个问题
→一个 std::const_pointer_cast 可以使用:

#include <iostream>
#include <memory>

template <typename T>
struct ContainerT {
  T a;
  ContainerT(T a): a(a) { }
};

template <typename T>
void print(std::shared_ptr<const ContainerT<T>> ref, std::ostream &out)
{
  out << "print: '" << ref->a << "'\n";
}

int main()
{
  print(std::make_shared<const ContainerT<int>>(42), std::cout);
  print(std::const_pointer_cast<const ContainerT<int>>(std::make_shared<ContainerT<int>>(42)), std::cout);
  return 0;
}

输出:

print: '42'
print: '42'

Live Demo on coliru


为了方便起见,常量转换可以在另一个函数模板中完成:

#include <iostream>
#include <memory>

template <typename T>
struct ContainerT {
  T a;
  ContainerT(T a): a(a) { }
};

template <typename T>
void print(std::shared_ptr<const ContainerT<T>> ref, std::ostream &out)
{
  out << "print const: '" << ref->a << "'\n";
}

template <typename T>
void print(std::shared_ptr<ContainerT<T>> ref, std::ostream &out)
{
  out << "print non-const: ";
  print(std::const_pointer_cast<const ContainerT<T>>(ref), out);
}

int main()
{
  print(std::make_shared<const ContainerT<int>>(42), std::cout);
  print(std::make_shared<ContainerT<int>>(42), std::cout);
  return 0;
}

输出:

print const: '42'
print non-const: print const: '42'

Live Demo on coliru

关于C++函数头匹配: how does matching work when const and templates are both involved?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55625109/

相关文章:

c++ - 函数指针指针数组

c++ - Variadic 模板模板和 SFINAE

c++ - 指向非静态成员变量的指针作为模板参数

Java 日期格式常量名称约定

c++ - 将 ip 地址转换为单个数字

c++ - 常量对象使什么常量?

c++ - C++中的构造函数和对象数组

c++ - 在模板链表中没有调用查找的匹配函数

c++ - 更改结构元素的常量

Objective-C 常量 : NSString comparison using ==?