0

I want to construct a graph whose Vertex is defined as follows:

struct Vertex {
    string key;              // key of the vertex.
    vector<string> adj;      // keys of adjacent vertices.
    vector<double> weights;  // weights of edges to adjacent vertices.

    Vertex(string x) : key(x) {}
    Vertex(const Vertex &other) {
        cout << "Copy Constructor\n";
        this->key = other.key;
        this->adj = other.adj;
        this->weights = other.weights;
    }
    Vertex &operator=(const Vertex &other) {
        cout << "Copy Assignment Operator\n";
        if (this != &other) {
            this->key = other.key;
            this->adj = other.adj;
            this->weights = other.weights;
        }
        return *this;
    }
};

First I define an unordered_map which has the pair of <string, Vertex> and string a = "a";.

I can construct the graph as follow:

graph.insert(make_pair(a, Vertex(a)));

But the following

graph[a] = Vertex(a);

doesn't work. And it outputs the error:

In file included from /usr/include/c++/9/bits/hashtable_policy.h:34,
                 from /usr/include/c++/9/bits/hashtable.h:35,
                 from /usr/include/c++/9/unordered_map:46,
                 from construct_graph.cpp:3:
/usr/include/c++/9/tuple: In instantiation of ‘std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&}; long unsigned int ..._Indexes1 = {0}; _Args2 = {}; long unsigned int ..._Indexes2 = {}; _T1 = const std::__cxx11::basic_string<char>; _T2 = Vertex]’:
/usr/include/c++/9/tuple:1663:63:   required from ‘std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&}; _Args2 = {}; _T1 = const std::__cxx11::basic_string<char>; _T2 = Vertex]’
/usr/include/c++/9/ext/new_allocator.h:146:4:   required from ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const std::__cxx11::basic_string<char>, Vertex>; _Args = {const std::piecewise_construct_t&, std::tuple<const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>, std::tuple<>}; _Tp = std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, Vertex>, true>]’
/usr/include/c++/9/bits/alloc_traits.h:483:4:   required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const std::__cxx11::basic_string<char>, Vertex>; _Args = {const std::piecewise_construct_t&, std::tuple<const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>, std::tuple<>}; _Tp = std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, Vertex>, true>; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, Vertex>, true> >]’
/usr/include/c++/9/bits/hashtable_policy.h:2086:36:   required from ‘std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type* std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_allocate_node(_Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, Vertex>, true> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, Vertex>, true>]’
/usr/include/c++/9/bits/hashtable_policy.h:701:8:   required from ‘std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type& std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::operator[](const key_type&) [with _Key = std::__cxx11::basic_string<char>; _Pair = std::pair<const std::__cxx11::basic_string<char>, Vertex>; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, Vertex> >; _Equal = std::equal_to<std::__cxx11::basic_string<char> >; _H1 = std::hash<std::__cxx11::basic_string<char> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, false, true>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type = Vertex; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = std::__cxx11::basic_string<char>]’
/usr/include/c++/9/bits/unordered_map.h:986:20:   required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](const key_type&) [with _Key = std::__cxx11::basic_string<char>; _Tp = Vertex; _Hash = std::hash<std::__cxx11::basic_string<char> >; _Pred = std::equal_to<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, Vertex> >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = Vertex; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = std::__cxx11::basic_string<char>]’
construct_graph.cpp:34:12:   required from here
/usr/include/c++/9/tuple:1674:70: error: no matching function for call to ‘Vertex::Vertex()’
 1674 |         second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
      |                                                                      ^
construct_graph.cpp:14:5: note: candidate: ‘Vertex::Vertex(const Vertex&)’
   14 |     Vertex(const Vertex &other) {
      |     ^~~~~~
construct_graph.cpp:14:5: note:   candidate expects 1 argument, 0 provided
construct_graph.cpp:13:5: note: candidate: ‘Vertex::Vertex(std::string)’
   13 |     Vertex(string x) : key(x) {}
      |     ^~~~~~
construct_graph.cpp:13:5: note:   candidate expects 1 argument, 0 provided

It seems that it can not find the constructor to construct a Vertex object in graph. But I don't understand why it cannot find the constructor.

Here is the complete code:

#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>

using namespace std;

struct Vertex {
    string key;              // key of the vertex.
    vector<string> adj;      // keys of adjacent vertices.
    vector<double> weights;  // weights of edges to adjacent vertices.

    Vertex(string x) : key(x) {}
    Vertex(const Vertex &other) {
        cout << "Copy Constructor\n";
        this->key = other.key;
        this->adj = other.adj;
        this->weights = other.weights;
    }
    Vertex &operator=(const Vertex &other) {
        cout << "Copy Assignment Operator\n";
        if (this != &other) {
            this->key = other.key;
            this->adj = other.adj;
            this->weights = other.weights;
        }
        return *this;
    }
};

void construct_graph() {
    unordered_map<string, Vertex> graph;
    string a = "a";
    // graph[a] = Vertex(a);  // doesn't work.
    graph.insert(make_pair(a, Vertex(a)));
}

// g++ construct_graph.cpp && ./a.out
int main(void) {
    construct_graph();
    return 0;
}
2

1 Answer 1

4

When graph[a] is called, it can't know in advance what you're going to do with the result (whether you're going to do = Vertex(a) or something else), so the value will first be constructed using its default constructor. Then = Vertex(a) performs assignment.

Other ways of inserting elements, such as .insert(), immediately receive the constructor arguments, so they can call the correct constructor directly.

The compiler doesn't generate a default constructor for your class, because you added a custom one. Having a default constructor is a good idea (even though using [] on maps is not; prefer .try_emplace() and .insert_or_assign() to all other methods), add it with Vertex() {}.

Also, don't manually define copy constructors/assignments, move constructors/assignments, and destructors, unless you actually need to (in this case you don't), because the compiler will likely do a better job at generating them. E.g. in this case by manually defining the copy operations you lost the move operations.

Sign up to request clarification or add additional context in comments.

2 Comments

It's possible that the OP defined their own copy c'tor and assignment so that they can check when/if they're being called (as the cout statements in them would indicate).
@AdrianMole Yep. Warning them just in case.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.