Recently I was studying about the benefits and uses of std::optional in C++17 (Bartek's Blog) and there I saw a line that said "std::optional can be used for Lazy Loading of Resources" . Upon digging a little bit, I saw that C++ does not have native support for Lazy evaluation. So I just gave it a try and wrote the following code for lazy evaluation in C++.

Lazy.ixx - Visual Studio(Experimental :Modules)

To compile this (VS2019):

cl /experimental:module /EHsc /TP /MD /std:c++latest Lazy.ixx /module:interface /module:output Lazy.pcm /Fo: Lazy.obj /c

#include<optional>
#include<functional>
export module Lazy;

export namespace gstd
{
template<typename T>
class Lazy
{
std::optional<T> m_resource;
std::function<T()>m_ctor_func;
public:
Lazy(std::function<T()>ctor_func)
{
m_ctor_func=ctor_func;
}
std::optional<T> operator ->()
{
//If resource is not initialized(i.e Ctor not invoked/ first time use)
if(!m_resource)
{
m_resource=m_ctor_func();
}
return m_resource;
}
};
}


Now sorry for using modules! (I just love them). To use this class it's pretty simple:

//main.cpp
#include<iostream>
import Lazy;

// A simple class called Resource
class Resource
{
public:
Resource()
{
std::cout<<"Welcome to new C++\n";
}
Resource(int a, int b)
{
std::cout<<"This also works : "<<a<<" "<<b<<"\n";
}
void func()
{
std::cout<<"Resource::func()\n";
}

};

int main()
{
gstd::Lazy<Resource>resx([](){ return Resource(4,5); });
std::cout<<"Before construction\n";

//Some code before using the resource

resx->func();
std::cin.get();
return 0;
}


To compile this:

cl /experimental:module /module:reference Lazy.pcm /std:c++latest /TP /MD /EHsc /c /Fo: main.obj main.cpp


Get the executable by linking those two files:

cl main.obj Lazy.obj


I am pretty much new to programming so please bear me with the silly mistakes. One problem that can be seen at the first glance is this class (Lazy) is not thread-safe. Implementing that would be easy with mutexes. But, other than that, how can I improve my code?

• Why are you storing/returning std::optional if you always return it with the value set? Oct 3, 2019 at 17:07
• I am returning std::optional<T> because of the problem with operator-> According to docs "It has additional, atypical constraints: It must return an object (or reference to an object) that also has a pointer dereference operator, or it must return a pointer that can be used to select what the pointer dereference operator arrow is pointing at." ** Now this behavior could be emulated by converting that to pointer and returning it but it will introduce a whole new set of problems among which the deadliest one is that people may get a false sense of assumption that the returned obj is a pointer Oct 4, 2019 at 0:21
• Which may make people do weird stuff like deleting that pointer or trying to assign something else. On the other hand I feel that returning a std::optional<T> would keep the users aware about what type of object they are dealing with Oct 4, 2019 at 0:23
• Also using std::optional sometimes does have a memory penalty due to alignment issues Oct 4, 2019 at 0:25
• Just return a T*, people can't do strange things with it without explicitly calling operator->. That's what the standard facilities do (and is the intended method). Oct 8, 2019 at 10:19

### 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.<global>'
before it is required
std::optional<T> m_resource;
^
main.cpp:26:24: note: in instantiation of template class 'gstd::Lazy<Resource>' requested here
gstd::Lazy<Resource>resx([](){ return Resource(4,5); });


I solved it by adding #include <optional> 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.

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 <typename T>
struct Lazy {
operator const T&() {
...
}
};

int main() {
Lazy<int> 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<int> const n(...);
int x = n + 2;


This implies an object that looks something like

template <typename T>
struct Lazy {
operator const T&() const {
...
}
private:
std::optional<T> 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<T()> 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.