I have written a small function template that joins different containers in a new container:
#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;
}
Using MSVC (cl.exe) with /std:c++17 the code compiles and works just fine. But when compiling with clang-6.0 or gcc-7.3, a compiler error is thrown. I.e. gcc says
no matching function for call to 'join(std::insert_iterator<std::unordered_set<std::__cxx11::basic_string<char> > >&)'
So obviously a function with this signature is not defined. But I don't get why it tries to call such a function. Shouldn't it be resolved like this
// 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);
Why does gcc try to instantiate join(std::insert_iterator<std::unordered_set<std::__cxx11::basic_string<char>>>&)
?
Here is an example using the compiler explorer.
When we are calling some function but there is not matching function definition argument, then we get compilation error as No matching function for call to c++. To resolve this error, We need to pass appropriate matching argument during function call.
This is a problem with MSVC. clang and g++ are correct. A lambda-expression whose smallest enclosing scope is a block scope (6.3.3) is a local lambda expression; any other lambda-expression shall not have a capture-default or simple-capture in its lambda-introducer.
Sometimes it also give that type mismatch or can not convert from one data type to another. You will get error for no matching function call when generally you are passing object / pointer / reference in function call and function definition is not able to match and accept that argument.
If function call and function definition arguments are not matching then you might get this error. Depend on compiler to compiler you might get different errors. Sometimes it also give that type mismatch or can not convert from one data type to another.
gcc and clang are correct. MSVC still has issues with proper template name lookup (i.e. "two-phase lookup").
join
, in join(iterator, containers...)
, is a dependent name. The candidates for finding that name are:
impl
as an associated namespace, so that wouldn't find the other overload either.In this situation, the fix is trivial: just reorder the two join()
overloads. This ensures that the 2-arg join()
will be found by that first bullet point.
Note that in C++17, you don't even need two overloads. Just one is fine:
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...);
}
}
Also consider using std::copy()
instead of the loop. Which would actually allow:
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), ...);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With