为什么重载解析在显式提供模板参数时会选择错误的重载?

问题描述 投票:0回答:1

我的代码在这里:

#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>

c++ templates c++17 std shared-ptr
1个回答
1
投票

问题在于您显式提供了模板参数

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);
通常最好推断模板参数,而不是显式提供它们。

© www.soinside.com 2019 - 2024. All rights reserved.