gcc 和 clang 抛出“没有匹配的函数调用”但 msvc (cl) 编译并按预期工作
Posted
技术标签:
【中文标题】gcc 和 clang 抛出“没有匹配的函数调用”但 msvc (cl) 编译并按预期工作【英文标题】:gcc and clang throw "no matching function call" but msvc (cl) compiles and works as expected 【发布时间】:2018-04-03 16:10:19 【问题描述】:我写了一个小函数模板,可以在一个新容器中加入不同的容器:
#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 是使用编译器资源管理器的示例。
【问题讨论】:
对它们重新排序的另一个好处是,您可以避免重复逻辑(即 for 循环)。 这是一个非常危险的实现,与 strcat 等优秀的旧 C 函数有同样的问题!什么,如果输出迭代器到达目标容器的结束迭代器??? @Aconcagua - 输出迭代器是一个插入器。我想这两个函数位于impl
命名空间中,这就是为什么它们“一般”很危险。
@Aconcagua 好吧,impl
命名空间中的函数旨在仅从全局范围函数 join
中使用,该函数使用 insert_iterator,这意味着它每次分配都会插入到容器中。
OK...我个人仍然会在模板参数中反映这一点。也许我有点安全***?旁注:短一点:auto it = std::back_inserter(container);
...
【参考方案1】:
gcc 和 clang 是正确的。 MSVC 仍然存在正确的模板名称查找问题(即“两阶段查找”)。
join
,在join(iterator, containers...)
中,是一个从属名称。找到该名称的候选者是:
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), ...);
【讨论】:
如果您使用 /permissive- 标志(位于项目设置的 c/c++->语言节点,标记为“一致性模式”),MSVC 会表现出正确的行为以上是关于gcc 和 clang 抛出“没有匹配的函数调用”但 msvc (cl) 编译并按预期工作的主要内容,如果未能解决你的问题,请参考以下文章
为啥 clang 和 gcc 在这个虚拟继承代码上存在分歧?
GCC 和 clang 上的 MSVC /Zp 替代方案是啥?