Lazy Resource Loading class in C++
Preface

I am using Apple Clang 10 and the following commands to build.

clang++ -std=c++17 -fmodules-ts --precompile Lazy.cppm -o Lazy.pcm
clang++ -std=c++17 -fmodules-ts -c Lazy.pcm -o Lazy.o
clang++ -std=c++17 -fmodules-ts -fprebuilt-module-path=. Lazy.o main.cpp


I'm using C++ 17 since that's the tag on the question, although I believe MSVC /std:c++latest corresponds to the parts of C++ 20 that are already implemented on MSVC.

I had one compilation error when I first tried this:

In file included from main.cpp:3:
Lazy.cppm:10:21: error: definition of 'optional' must be imported from module 'Lazy.&lt;global&gt;'
      before it is required
   std::optional&lt;T&gt; m_resource;
                    ^
main.cpp:26:24: note: in instantiation of template class 'gstd::Lazy&lt;Resource&gt;' requested here
   gstd::Lazy&lt;Resource&gt;resx([](){ return Resource(4,5); });


I solved it by adding #include &lt;optional&gt; in main.cpp, but maybe there's a better way.  Please let me know if there is a better solution.  I have only toyed around with modules so I'm far from an expert.

Fix your indentation

Minor nitpick: use consistent indentation.
You have 4 spaces for most of it (which is fine) but 1 space in a few spots.
And 0 spaces in a few spots too.  There are tools that can do this for you automatically (although it's pretty
easy to just do it by hand).

Consider using operator T

C++ can implicitly convert your object into a T:

template &lt;typename T&gt;
struct Lazy {
    operator const T&amp;() {
        ...
    }
};

int main() {
    Lazy&lt;int&gt; n(...);
    return n;
}


Consider making access const

When you access an object, you don't expect to modify it.  In other words, you should be able to write:

Lazy&lt;int&gt; const n(...);
int x = n + 2;


This implies an object that looks something like

template &lt;typename T&gt;
struct Lazy {
    operator const T&amp;() const {
        ...
    }
private:
    std::optional&lt;T&gt; mutable opt;
};


Think about what kind of functions you want to support

As you've written it now, you have two std::functions: the argument to the ctor and the one in the object.  You should at most have one.  You could use std::move.

Lazy(std::function&lt;T()&gt; ctor_func)
    : m_ctor_func(std::move(ctor_func)) // at least do this!
{}


At least use a member initializer list instead of initialization by assignment.

It may be preferable to get rid of std::function completely and instead use a template parameter.
This would allow you to get rid of all the copying.
You could even have non-copyable types in the function object.
You could even go overboard and deduce the stored type based on the templated function.  Then you could just write:

Lazy const n([nc=NonCopyable()]{ return 1; });


No template arguments!  It's up to you to decide whether this is a good idea, but it may be a good exercise if you
are new to templates.

Use optional::emplace

... instead of assignment.