gcc 和 clang 抛出 "no matching function call" 但 msvc (cl) 编译并按预期工作
gcc and clang throw "no matching function call" but msvc (cl) compiles and works as expected
我写了一个小函数模板,可以在一个新的容器中连接不同的容器:
#include <vector>
#include <unordered_set>
#include <string>
#include <iostream>
#include <iterator>
namespace impl
{
template <typename OutIterator, typename Container, typename ...Containers>
void join(OutIterator iterator, const Container& container, const Containers& ...containers)
{
for (const auto& item : container)
*iterator++ = item;
join(iterator, containers...); // gcc and clang cannot resolve this call
}
template <typename OutIterator, typename Container>
void join(OutIterator iterator, const Container& container)
{
for (const auto& item : container)
*iterator++ = item;
}
}
template <typename OutContainer, typename ...Containers>
OutContainer join(const Containers& ...containers)
{
OutContainer container;
auto it = std::inserter(container, container.end());
impl::join(it, containers...);
return container;
}
int main()
{
using namespace std;
vector<string> a = {"one"s, "two"s, "three"s};
unordered_set<string> b = {"four"s, "five"s };
auto res = join<unordered_set<string>>(a, b);
for (auto& i : res)
cout << i << "\n";
return 0;
}
将 MSVC (cl.exe) 与 /std:c++17 结合使用,代码编译并运行良好。但是用clang-6.0或gcc-7.3编译时,会抛出编译错误。 IE。 gcc 说
no matching function for call to 'join(std::insert_iterator<std::unordered_set<std::__cxx11::basic_string<char> > >&)'
显然没有定义具有此签名的函数。但我不明白为什么它会尝试调用这样的函数。不应该这样解决吗
// in main()
join<unordered_set<string>>(a, b);
unordered_set<string> join(const vector<string>& a, const unordered_set<string>& b);
void impl::join(std::insert_iterator<unordered_set<string>> iterator, const vector<string>& a, const unordered_set<string>& b);
void impl::join(std::insert_iterator<unordered_set<string>> iterator, const unordered_set<string>& b);
为什么 gcc 会尝试实例化 join(std::insert_iterator<std::unordered_set<std::__cxx11::basic_string<char>>>&)
?
Here 是使用编译器资源管理器的示例。
gcc 和 clang 是正确的。 MSVC 在正确的模板名称查找方面仍然存在问题(即 "two-phase lookup")。
join
,在join(iterator, containers...)
中,是从属名。找到那个名字的候选人是:
- 模板定义点的所有名称。这个查找只是找到它自己(可变参数重载),而不是另一个重载(2-arg 重载),因为它还没有被声明。
- ADL 在其参数上可以找到的所有名称。 None 这些参数有
impl
作为关联的命名空间,因此也不会找到其他重载。
在这种情况下,修复很简单:只需重新排序两个 join()
重载。这确保了 2-arg join()
将被第一个要点找到。
请注意,在 C++17 中,您甚至不需要两个重载。一个就好:
template <typename OutIterator, typename Container, typename... Containers>
void join(OutIterator iterator, Container const& container, Container const&... containers) {
for (auto const& item : container) {
*iterator++ = item;
}
if constexpr(sizeof...(Containers) > 0) {
join(iterator, containers...);
}
}
也可以考虑使用 std::copy()
而不是循环。这实际上允许:
template <typename OutIterator, typename... Containers>
void join(OutIterator iterator, Container const&... containers) {
using std::begin;
using std::end;
(iterator = std::copy(begin(containers), end(containers), iterator), ...);
}
我写了一个小函数模板,可以在一个新的容器中连接不同的容器:
#include <vector>
#include <unordered_set>
#include <string>
#include <iostream>
#include <iterator>
namespace impl
{
template <typename OutIterator, typename Container, typename ...Containers>
void join(OutIterator iterator, const Container& container, const Containers& ...containers)
{
for (const auto& item : container)
*iterator++ = item;
join(iterator, containers...); // gcc and clang cannot resolve this call
}
template <typename OutIterator, typename Container>
void join(OutIterator iterator, const Container& container)
{
for (const auto& item : container)
*iterator++ = item;
}
}
template <typename OutContainer, typename ...Containers>
OutContainer join(const Containers& ...containers)
{
OutContainer container;
auto it = std::inserter(container, container.end());
impl::join(it, containers...);
return container;
}
int main()
{
using namespace std;
vector<string> a = {"one"s, "two"s, "three"s};
unordered_set<string> b = {"four"s, "five"s };
auto res = join<unordered_set<string>>(a, b);
for (auto& i : res)
cout << i << "\n";
return 0;
}
将 MSVC (cl.exe) 与 /std:c++17 结合使用,代码编译并运行良好。但是用clang-6.0或gcc-7.3编译时,会抛出编译错误。 IE。 gcc 说
no matching function for call to 'join(std::insert_iterator<std::unordered_set<std::__cxx11::basic_string<char> > >&)'
显然没有定义具有此签名的函数。但我不明白为什么它会尝试调用这样的函数。不应该这样解决吗
// in main()
join<unordered_set<string>>(a, b);
unordered_set<string> join(const vector<string>& a, const unordered_set<string>& b);
void impl::join(std::insert_iterator<unordered_set<string>> iterator, const vector<string>& a, const unordered_set<string>& b);
void impl::join(std::insert_iterator<unordered_set<string>> iterator, const unordered_set<string>& b);
为什么 gcc 会尝试实例化 join(std::insert_iterator<std::unordered_set<std::__cxx11::basic_string<char>>>&)
?
Here 是使用编译器资源管理器的示例。
gcc 和 clang 是正确的。 MSVC 在正确的模板名称查找方面仍然存在问题(即 "two-phase lookup")。
join
,在join(iterator, containers...)
中,是从属名。找到那个名字的候选人是:
- 模板定义点的所有名称。这个查找只是找到它自己(可变参数重载),而不是另一个重载(2-arg 重载),因为它还没有被声明。
- ADL 在其参数上可以找到的所有名称。 None 这些参数有
impl
作为关联的命名空间,因此也不会找到其他重载。
在这种情况下,修复很简单:只需重新排序两个 join()
重载。这确保了 2-arg join()
将被第一个要点找到。
请注意,在 C++17 中,您甚至不需要两个重载。一个就好:
template <typename OutIterator, typename Container, typename... Containers>
void join(OutIterator iterator, Container const& container, Container const&... containers) {
for (auto const& item : container) {
*iterator++ = item;
}
if constexpr(sizeof...(Containers) > 0) {
join(iterator, containers...);
}
}
也可以考虑使用 std::copy()
而不是循环。这实际上允许:
template <typename OutIterator, typename... Containers>
void join(OutIterator iterator, Container const&... containers) {
using std::begin;
using std::end;
(iterator = std::copy(begin(containers), end(containers), iterator), ...);
}