gcc和clang抛出"没有匹配的函数调用",但msvc(cl)编译并按预期工作

Tim*_*imo 8 c++ gcc clang cl c++17

我编写了一个小函数模板,它将新容器中的不同容器连接起来:

#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;
}
Run Code Online (Sandbox Code Playgroud)

使用MSVC(cl.exe)和/ std:c ++ 17代码编译并正常工作.但是当使用clang-6.0或gcc-7.3进行编译时,会抛出编译器错误.即gcc说

no matching function for call to 'join(std::insert_iterator<std::unordered_set<std::__cxx11::basic_string<char> > >&)'
Run Code Online (Sandbox Code Playgroud)

显然,没有定义具有此签名的函数.但我不明白为什么它试图调用这样的功能.不应该这样解决

// 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);
Run Code Online (Sandbox Code Playgroud)

为什么gcc尝试实例化join(std::insert_iterator<std::unordered_set<std::__cxx11::basic_string<char>>>&)

以下是使用编译器资源管理器的示例.

Bar*_*rry 13

gcc和clang是正确的.MSVC仍然存在正确的模板名称查找问题(即"两阶段查找").

join,in join(iterator, containers...),是一个从属名称.找到该名称的候选人是:

  • 模板定义点的所有名称.这个查找只是找到它自己(可变重载),而不是另一个重载(2-arg),因为它还没有被声明.
  • ADL可以在其参数上找到的所有名称.这些参数都没有impl作为关联的命名空间,因此也不会找到其他重载.

在这种情况下,修复是微不足道的:只需重新排序两个join()重载.这确保了join()第一个项目符号点可以找到2-arg .


请注意,在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...);
    }
}
Run Code Online (Sandbox Code Playgroud)

还要考虑使用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), ...);
}
Run Code Online (Sandbox Code Playgroud)