我的代码在这里:
#include <iostream>
#include <memory>
#include <queue>
template<typename TValue>
[[maybe_unused]]
constexpr auto t1(const std::queue<TValue> &value) -> void {
std::queue<TValue> temp = value;
while (!temp.empty()) {
std::cout << temp.front() << std::endl;
temp.pop();
}
}
template<typename TValue>
constexpr auto t1(const TValue &nm) -> void {
std::cout << nm << std::endl;
}
template<typename TValue>
constexpr auto t1(const std::shared_ptr<TValue> &nm) -> void {
t1<TValue>(*nm);
}
int main(int argc, char **argv) {
std::shared_ptr<const std::string> s_ptr = std::make_shared<const std::string>("test");
t1(s_ptr);
return 0;
}
此代码无法编译(https://godbolt.org/z/crvKb7rEz):
错误 C2338:static_assert 失败:“C++ 标准禁止 const 元素的容器,因为分配器格式不正确。”
我尝试使用
shared_ptr
更改模板,例如:
template<typename TValue>
constexpr auto t1(const std::shared_ptr<TValue> &nm) -> void {
TValue temp = *nm;
t1<TValue>(temp);
}
它会导致同样的错误。 我也尝试过获取“TValue”类型:
template<typename TValue>
constexpr auto t1(const std::shared_ptr<TValue> &nm) -> void {
if constexpr (std::is_same_v<TValue, const std::string>){
static_assert(false, "??");
}
t1<TValue>(*nm);
}
static_assert
被触发。
这意味着“TValue”是“const std::string”。
不小心,我像这样删除了
<TValue>
:
template<typename TValue>
constexpr auto t1(const std::shared_ptr<TValue> &nm) -> void {
t1(*nm);
}
或者:
template<typename TValue>
constexpr auto t1(const std::shared_ptr<TValue> &nm) -> void {
t1<std::remove_cv_t<TValue>>(*nm);
}
这两者都有效。
为什么模板有
const std::string
时要把std::queue
转换为<TValue>
?
问题在于您显式提供了模板参数
TValue
,这会导致 std::deque<const std::string>
的实例化。
std::deque<T>
需要一个非常量、非易失性 T
,正如失败的静态断言所示:
错误:静态断言失败:
必须具有非常量、非易失性std::deque
value_type
- https://godbolt.org/z/PGY7nKW57
为了说明发生了什么,这是您问题的简化版本(https://godbolt.org/z/zYPhhb5dq):
#include <type_traits>
template <typename T>
struct container {
static_assert(not std::is_const_v<T>);
};
template<typename T> void foo(container<T>);
template<typename T> void foo(const T &nm);
int main() {
foo<const int>(0);
}
error: static assertion failed due to requirement '!std::is_const_v<const int>'
5 | static_assert(not std::is_const_v<T>);
| ^~~~~~~~~~~~~~~~~~~~~~
note: in instantiation of template class 'container<const int>' requested here
15 | foo<const int>(0);
|
您可以写
t1(*nm)
来代替。
这里的区别在于 std::deque<const std::string>
永远不会被实例化。
相反,替换为 t1(std::deque)
将会失败,因为 *nm
是 const std::string
并且 TValue
中的 std::deque<TValue>
无法从 const std::string
推导出来。
template<typename TValue>
constexpr auto t1(const std::shared_ptr<TValue> &nm) -> void {
t1(*nm); // OK
}
t1<std::remove_cv_t<TValue>>(*nm);
也可以工作(尽管你不应该写它),因为你实例化了 std::deque<std::string>
。
这是有效的,但是,重载解析将选择 t1(const T&)
,因为没有从 std::string
到 std::deque
的隐式转换。
你应该写
t1(*nm);
通常最好推断模板参数,而不是显式提供它们。