The Author Online Book Forums are Moving

The Author Online Book Forums will soon redirect to Manning's liveBook and liveVideo. All book forum content will migrate to liveBook's discussion forum and all video forum content will migrate to liveVideo. Log in to liveBook or liveVideo with your Manning credentials to join the discussion!

Thank you for your engagement in the AoF over the years! We look forward to offering you a more enhanced forum experience.

Diego Saraiva (4) [Avatar] Offline
The code into listing 6.3 presented some issues during the compilation. On GCC-7 the compiler reclaims:

warning: there are no arguments to 'm_computation' that depend on a template parameter, so a declaration of 'm_computation' must be available [-fpermissive]

The trouble is even worst into clang. The compiler doesn't accept it printing the following message:

use of undeclared identifier 'm_computation' operator decltype(m_computation()) () const

So, I made a something like a "forward definition" and it works. I think that you would like to know about this issue.

template<typename F>
class lazy_val
    F m_computation; // forward definition
        lazy_val(F computation) : m_computation(computation), m_cache_initialized(false)
    operator decltype(m_computation()) () const
        std::unique_lock<std::mutex> lock{m_cache_mutex};
        if(not m_cache_initialized)
            m_cache = m_computation();
            m_cache_initialized = true;
        return m_cache;
        mutable bool m_cache_initialized;
        mutable decltype(m_computation()) m_cache;
        mutable std::mutex m_cache_mutex;

My default C++ compiler:

Apple LLVM version 8.1.0 (clang-802.0.42)
Target: x86_64-apple-darwin16.6.0
Thread model: posix

Thanks for your attention.
Best Regards
Diego Saraiva.
Ivan Cukic (104) [Avatar] Offline
Hi Diego,

Thanks for this. I've switched the privates to be above the public parts altogether. There will be a full lazy_val implementation in the accomapnying code examples (a bit different than this one - it will use optional<T>smilie.

Sumant Tambe (17) [Avatar] Offline
Should the lazyval::operator() return a const reference?
Ivan Cukic (104) [Avatar] Offline
Yes, a operator const T& could be useful. I'll see to add it.

Pascal Menuet (6) [Avatar] Offline
I think there are errors/non-optimal code in figure Listing 6.2:
- the function make_lazy_val should return lazy_val<std::decay_t<F>> or else it produce strange things with l-value arguments.
- the constructor of lazy_val should std::move its parameter (or even template it and perfect-forward it)

Another similar issue in Listing 6.9: the constructor of memoize_helper does not forward its first parameter f
Ivan Cukic (104) [Avatar] Offline
I'm torn on this one.

With the decay, the lazy_val will copy the function object (safer, but less efficient), and without it will save the reference to it (efficient, not safe).

You are right about the moves. Will see what to do about it - I tried to make the snippets in the book simpler, but this was maybe a wrong place to simplify.
Pascal Menuet (6) [Avatar] Offline
To my mind, if you don't use decay_t, there is a deeper problem of consistency:
- if the argument provided to make_lazy_val is an r-value then the type F will be deduced as a plain type, so you will store a plain type inside lazy_val.
- if the argument provided to make_lazy_val is an l-value then the type F will be deduced as an l-value reference, so you will store a reference inside lazy_val.
Ivan Cukic (104) [Avatar] Offline
I agree. I'll probably change it, and if someone wants a reference, there is always std::ref - to mimic the behaviour of STL algorithms.
Hans-J. Schmid (16) [Avatar] Offline
Hi Ivan,

could you explain in some more words how this macro works?

#define lazy make_lazy_val_helper - [=]

Kind regards!
Ivan Cukic (104) [Avatar] Offline
make_lazy_val_helper is an instance of an object which has a operator- defined on it (it can be declared as an inline variable in C++17).

This is a dummy object. The only important thing is the operator- defined on it. It takes a function object, and creates an instance of lazy_val.

The macro calls the operator- on the dummy object, and as the right argument, it begins to define a lambda - it defines the lambda head which captures everything, and leaves the body to be defined later.

auto v = lazy {
    return 42;

gets expanded into

auto v = make_lazy_val_helper - [=] {
    return 42;

which, if we write the operator call as a normal function call, becomes

auto v = make_lazy_val_helper.operator-(
    [=] {
        return 42;

And this (because of how operator- is implemented) is the same as:

auto v = make_lazy_val(
    [=] {
        return 42;

Tricks like these can be quite cool, but they should not be overused.
Hans-J. Schmid (16) [Avatar] Offline
OK, got it. Thanks a lot!