C++ shared pointer wrapper for lazy initialization
A really useful idea - well worth creating.
Missing #include &lt;memory&gt;.  With that fixed, I get almost clean compilation:
255367.cpp:9:5: warning: ‘LazySharedPtr&lt;int, int&gt;::init’ should be initialized in the member initialization list [-Weffc++]

I agree, we should use the initialisation list for init:
LazySharedPtr(Args ... args)
    : init{[args = std::make_tuple(std::forward&lt;Args&gt;(args) ...)]() mutable
           {
               return std::apply([](auto&amp;&amp; ... args) {
                                     return std::make_shared&lt;T&gt;(std::forward&lt;Args&gt;(args) ...);
                                 }, std::move(args));
           }},
      ptr{}
{}

I don't see why we have the inner lambda.  There's no good reason not to pass std::make_shared() directly to std::apply(), like this:
LazySharedPtr(Args ... args)
    : init{[args = std::make_tuple(std::forward&lt;Args&gt;(args) ...)]() mutable
           {
               return std::apply(std::make_shared&lt;T, Args...&gt;, std::move(args));
           }},
      ptr{}
{}

I think the class could be easier to use.  There's no need for the argument types to be part of the class itself, as they are erased by std::function.  So make the class template have only T as argument.  Otherwise we need separate code paths for our smart pointers if they are created differently.  Much better to separate the type that is essential to the LazySharedPtr from those which are accidental:
template &lt;class T&gt;
class LazySharedPtr {
public:
    template &lt;typename ... Args&gt;
    LazySharedPtr(Args ... args);
}

There's some functionality missing that I would expect from a drop-in replacement for std::shared_ptr:

operator*
get()
reset()

I'd also expect a LazySharedPtr to be assignable to std::shared_ptr (invoking the creator function in the process).  That could reduce the need for these functions (especially reset(), which might not make sense here).
On the other hand, I don't think that LazySharedPtr is intended as a base class for inheritance, so shouldn't need a virtual destructor.  And we don't need IsInited(), given we already have operator bool.
Do have a think about what it means to copy a lazy-pointer object.  As it stands, copying a materialized instance yields another shared pointer to the same T object, but copying an unmaterialized instance will result in different T objects in the original and the copy.  I wonder if this could make it difficult to use correctly; we might want to make this a move-only type, and require casting to std::shared_ptr in order to copy (thus materialising the object).
For efficiency, InitAndGet() shouldn't copy the shared pointer, but return a reference.  I'm not fond of the naming - C++ convention uses snake_case for function names.
There's a lot of unnecessary this-&gt; cluttering the code.

Modified code
#include &lt;cstddef&gt;
#include &lt;functional&gt;
#include &lt;memory&gt;
#include &lt;tuple&gt;
#include &lt;utility&gt;

template &lt;class T&gt;
class LazySharedPtr {
public:
    template &lt;typename... Args&gt;
    LazySharedPtr(Args... args)
        : ptr{},
          init{[args = std::make_tuple(std::move&lt;Args&gt;(args)...)]() mutable
               { return std::apply(std::make_shared&lt;T, Args...&gt;, std::move(args)); }}
    {}

    // compiler-defaulted copy/move construct and assign, and destructor

    auto operator-&gt;() const
    { return object().get(); }

    auto operator*() const
    { return *object(); }

    auto operator[](std::ptrdiff_t idx)
    { return object()[idx]; }

    explicit operator bool() const noexcept
    { return ptr; }

    explicit operator std::shared_ptr&lt;T&gt;() const
    { return object(); }

private:
    mutable std::shared_ptr&lt;T&gt; ptr;
    std::function&lt;std::shared_ptr&lt;T&gt;()&gt; init;

    auto&amp; object() const
    {
        if (!ptr) { ptr = init(); }
        return ptr;
    }
};


int main()
{
    LazySharedPtr&lt;int&gt; a{0};
    auto b = std::shared_ptr&lt;int&gt;{a};
    return *b;
}