Storing a copy of unique_ptr When one writes c++ code to manipulate LinkedLists, Trees, etc. one creates a tmp pointer that points to the start/root and changes that pointer as one traverses along.
What would be the equivalent way of doing that in smart pointers?
For example, I wrote a Trie code using unique_ptrs to understand how to pass pointers around. To make the program work, I had to dive inside the smart pointer and access the underlying raw pointer. Is this the right way of doing things? Or is it infact possible to pass the unique_ptr around. I believe std::move will not be a good idea here since it will a destructive operation.
My code is as follows. Special call out to .get() op to get the raw pointer.
#include &lt;iostream&gt;
#include &lt;memory&gt;
#include &lt;map&gt;

class TrieNode
{
public:
    bool isLeaf{ false };
    std::map&lt;char, std::unique_ptr&lt;TrieNode&gt;&gt; children;
};

class Trie
{
    std::unique_ptr&lt;TrieNode&gt; root{nullptr};

public:
    Trie()
    {
        root.reset(new TrieNode());
    }

    void insert(const std::string&amp; word)
    {
        auto node = root.get();
        for (auto ch : word)
        {
            if (node-&gt;children.find(ch) == node-&gt;children.end())
            {
                node-&gt;children[ch] = std::make_unique&lt;TrieNode&gt;();
            }
            node = node-&gt;children[ch].get();
        }
        node-&gt;isLeaf = true;
    }

    bool search(const std::string&amp; word) const
    {
        auto node = root.get();
        for (auto ch : word)
        {
            if (node-&gt;children.find(ch) == node-&gt;children.end())
            {
                return false;
            }
            node = node-&gt;children[ch].get();
        }
        return (node &amp;&amp; node-&gt;isLeaf);
    }
};

int main()
{
    Trie trie;
    trie.insert(&quot;application&quot;);
    std::cout &lt;&lt; &quot;does apple there in the trie? &quot; &lt;&lt; (trie.startsWith(&quot;app&quot;) ? &quot;yes&quot; : &quot;no&quot;) &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;is application there in the trie? &quot; &lt;&lt; (trie.search(&quot;apple&quot;) ? &quot;yes&quot; : &quot;no&quot;) &lt;&lt; std::endl;

    return 0;
}
```
The details of TrieNode don't need to be visible outside of Trie, so I recommend making it a private (or protected) member type Trie::Node.

We don't need to write a constructor, if we initialise the root node with the value we want:
std::unique_ptr&lt;TrieNode&gt; root = std::make_unique&lt;TrieNode&gt;();


Some coding styles ask us to indicate when auto resolves to a pointer type:
    auto* node = root.get();


Prefer passing a std::string_view than a const std::string&amp; - it saves having to construct a string object in many circumstances (including the const char* string literals used in this program).  Either way, we're missing the required header file, and only succeed by dumb luck.

We're doing a lookup twice here:

        if (node-&gt;children.find(ch) == node-&gt;children.end())
        {
            node-&gt;children[ch] = std::make_unique&lt;TrieNode&gt;();
        }
        node = node-&gt;children[ch].get();


If we keep the result of find(), then we can use that iterator for that last line, instead of finding it all over again with [].  Perhaps a good idea to use insert() for adding the new node; that gives you back an iterator:
    auto* node = root.get();
    for (auto const ch: word) {
        auto&amp; children = node-&gt;children;
        auto it = children.find(ch);
        if (it == children.end()) {
            it = children.emplace(ch, std::make_unique&lt;TrieNode&gt;()).first;
        }
        node = it-&gt;second.get();
    }

Similarly, in search():
bool search(const std::string_view word) const
{
    auto* node = root.get();
    for (auto const ch: word) {
        auto&amp; children = node-&gt;children;
        auto it = children.find(ch);
        if (it == children.end()) {
            return false;
        }
        node = it-&gt;second.get();
    }
    return node-&gt;isLeaf;
}


This call:

trie.startsWith(&quot;app&quot;)


This fails to compile, as there's no such member.  It looks like part of the code is missing, which is certainly a sign that testing could be improved.

Don't flush streams unnecessarily using std::endl when a plain newline will do.

Improved program
#include &lt;iostream&gt;
#include &lt;map&gt;
#include &lt;memory&gt;
#include &lt;string_view&gt;

class Trie
{
    struct Node
    {
        bool isLeaf = false;
        std::map&lt;char, std::unique_ptr&lt;Node&gt;&gt; children = {};
    };

    std::unique_ptr&lt;Node&gt; root = std::make_unique&lt;Node&gt;();

public:
    void insert(const std::string_view word)
    {
        auto* node = root.get();
        for (auto const ch: word) {
            auto&amp; children = node-&gt;children;
            auto it = children.find(ch);
            if (it == children.end()) {
                it = children.emplace(ch, std::make_unique&lt;Node&gt;()).first;
            }
            node = it-&gt;second.get();
        }
        node-&gt;isLeaf = true;
    }

    bool search(const std::string_view word) const
    {
        auto* node = root.get();
        for (auto const ch: word) {
            auto&amp; children = node-&gt;children;
            auto it = children.find(ch);
            if (it == children.end()) {
                return false;
            }
            node = it-&gt;second.get();
        }
        return node-&gt;isLeaf;
    }
};

int main()
{
    Trie trie;
    trie.insert(&quot;application&quot;);

    for (auto const s: {&quot;app&quot;, &quot;apple&quot;, &quot;application&quot;}) {
        std::cout &lt;&lt; s &lt;&lt; (trie.search(s) ? &quot; is present\n&quot; : &quot; not found\n&quot;);
    }
}