C ++函数头匹配:当涉及const和模板时,匹配是如何工作的?

问题描述 投票:2回答:2

我有一个模板化的功能,我希望打电话。这是标题的(精简版):

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!

另一方面,如果我省略constness,模板化版本确实有效:

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

但我应该能够在const事物上编写一个函数并传递一个非const值(函数将不会修改),对吧?实际上,如果我回到非托管指针,那么写入标头的相应旧方法就是这样

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似乎产生相同的结果。)

c++ templates const shared-ptr
2个回答
2
投票

关于pointee的常量,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


为方便起见,const-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 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


1
投票

这是一个简化的代码:

template <typename T>
void f(std::shared_ptr<const std::vector<T>>) { }

void g(std::shared_ptr<const std::vector<int>>) { }

int main() {
  f(std::make_shared<std::vector<int>>()); // ERROR
  g(std::make_shared<std::vector<int>>()); // OK
}

要了解发生了什么,请阅读错误消息,例如,由g ++打印的错误消息:

...
note:   template argument deduction/substitution failed:
note:   types 'const std::vector<T>' and 'std::vector<int>' have incompatible cv-qualifiers

它告诉你问题是模板参数推导/替换。 C ++规则似乎不允许这种类型的推论。 (如果我有时间,我会尝试找到标准的相关部分)。


但是,您可以通过提供显式模板参数来跳过模板参数推导:

f<int>(std::make_shared<std::vector<int>>()); // OK
© www.soinside.com 2019 - 2024. All rights reserved.