我的代码在这里:
#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 int> s_ptr = std::make_shared<const int>(7);
t1(s_ptr);
return 0;
}
此代码无法编译( https://godbolt.org/z/crvKb7rEz ):
error C2338: static_assert failed: 'The C++ Standard forbids containers of const elements because allocator is ill-formed.'
我尝试使用 shared_ptr
更改模板像:
template<typename TValue>
constexpr auto t1(const std::shared_ptr<TValue> &nm) -> void {
const int 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 int>){
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);
}
另外:
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 int> s_ptr = std::make_shared<const int>(7);
t1(s_ptr);
return 0;
}
所有这些都有效。
为什么模板要使用std::queue
过载时<TValue>
等于const int
?我除了它使用:
template<typename TValue>
constexpr auto t1(const TValue &nm) -> void {
std::cout << nm << std::endl;
}
最佳答案
问题是您显式提供了模板参数 TValue
这会导致 std::deque<const std::string>
的实例化。
std::deque<T>
requires a non-const, non-volatile T
,正如失败的静态断言所示:
error: static assertion failed:
std::deque
must have a non-const, non-volatilevalue_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(T);
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);
通常最好推断模板参数,而不是显式提供它们。
关于 SFINAE 友好性的说明
根本问题是static_assert
不是 SFINAE 友好的,即它会导致替换后出现错误。
在 C++20 中,可以使用约束代替 static_assert
同时具有相同的模板参数:
template <typename T>
requires (not std::is_const_v<T>) // analogous for std::deque
struct container { };
如果这样做,foo<const int>(0)
有效 ( https://godbolt.org/z/TWKxs99Yj )。
我不认为 std::deque
是合法的有这样的限制。
如果有它们,您的代码就可以工作。
关于c++ - 为什么在显式提供模板参数时重载解析会选择错误的重载?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77751095/