2
votes

In the header, I have

class CSomeClass
{
    const GUID m_guid;

public:
    CSomeClass();
///...
}

And in the source file

CSomeClass::CSomeClass()
    , m_guid(
        []() {
        GUID g;
        ::CoCreateGuid(&g);
        return g;
        }()
    )
{
}

As you know Guids can be used as identifications not meant to be changed. Given the ::CocreateGuid() function provides what I want as an output parameter, instead of returning it, I cannot use directly a simple call to the function for initializing the m_guid member field, that is constant.

So, a consequence of its constness, is that it must be initialized before the opening bracket in initializer list, and therefore not be simply assigned with a call to ::CocreateGuid() in the constructor body.

Is there a simpler way to initialize it than this lambda expression?

5
@IgorTandetnik: Did you think I did not try what you said? Try and see.sergiol
My initial reaction is "good grief". Just don't do it. Initialise it in the constructor, not the initializer list. Do yourself or whomever has to read and maintain your code a favour.Robinson
You may still write in a real utility function.Jarod42
Right. Never mind. const already kicks in in the constructor body. I'm with @Jarod42 - write private: static GUID CSomeClass::MakeGUID() and use that to initialize m_guidIgor Tandetnik
I'm also with @Jarod42 and @IgorTandetnik on having a utility function. However, instead of a private static member function I would completely hide it in the source file (.cpp). Make it either static in the .cpp or put it in an anonymous namespace also in the .cpp.Cassio Neri

5 Answers

2
votes

When the lambda expression is correct, I would use a helper function for that:

GUID create_guid()
{
    GUID g;
    ::CoCreateGuid(&g);
    return g;
}

CSomeClass::CSomeClass() : m_guid(create_guid()) {}

In addition, create_guid() has a meaning by itself and could be reused (even if making it a implementation detail is possible/correct).

1
votes

You should consider wrapping the GUID in its own class:

class CGUID
{
public:
    CGUID()
    {
        CoCreateGuid(m_guid);
    }

    const GUID& guid() const { return m_guid; }
    // Maybe some useful functions:
    bool operator==(const CGUID&) const;

private:
    GUID m_guid;
};

Now you can use the above as a member:

class CSomeClass
{
    const CGUID m_guid;
...
0
votes

Here we abstract your pattern:

template<class A>
A co_make( HRESULT(*f)(A*) {
  A a;
  HRESULT hr = f(&a);
  Assert(SUCCEEDED(hr));
  if (!SUCCEEDED(hr))
    throw hr;
  return a;
}

CSomeClass::CSomeClass()
  m_guid(
    co_make(&::CoCreateGuid)
  )
{}

where we detect failure and assert then throw if that is the case.

I'm not sure this is simpler.

Really, write a GUID make_guid() function, stick it in some header, and call it.

0
votes

Your proposal is the simplest way to initialize the constant instance member.

Don't get scared of lambdas, as a matter of fact, in general it is a new style recommendation to use lambdas for complex initializations of constants and references because they share the property of only being initialized at the point of declaration (or instance member initialization in the initializer list).

Furthermore, your code triggers the "named return value optimization" and there is no copy construction at the return from the lambda.

The interface to CoCreateGuid is deficient because it requires an output argument.

If you insist on not using the lambda, I think the next most practical alternative is to, in the constructor body, de-constify using const_cast to pass it to CoCreateGuid.

Mind you that one you enter the body of a constructor the language considers all individual members to have been properly initialized, and will invoke destructors for them should an exception happens, this makes a very big difference whether something is initialized in the initializer list or left with a binary pattern of garbage.

Finally, unfortunately you can't just call CoCreateGuid with a de-constified reference to m_guid in the lambda, because the lambda will still return a value and that will overwrite the member. It is essentially the same as what you already wrote (with the exception of the default constructor of g)

-1
votes

It would be simpler if you declare m_guid as a mutable instance member as opposed to const. The difference is that mutable are like a const for users of a class but a perfectly fine lvalue within the class