我有一个想要调用的模板化函数。这是标题的精简版本:
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似乎会产生相同的结果。)
答案 0 :(得分: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'
对于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'
为方便起见,可以在另一个功能模板中进行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'
答案 1 :(得分: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