Код проще, чем lambda для вызова в конструкторе, который использует функцию выходного параметра для инициализации члена const

В заголовке у меня есть

class CSomeClass
{
    const GUID m_guid;

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

И в исходном файле

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

Как вы знаете, Гиды могут использоваться как идентификаторы, которые не должны быть изменены. Учитывая, что функция ::CocreateGuid() предоставляет то, что я хочу в качестве выходного параметра, вместо того, чтобы возвращать ее, я не могу напрямую использовать простой вызов функции для инициализации поля члена m_guid, которое является постоянным.

Таким образом, следствием своей константы является то, что она должна быть инициализирована перед открывающей скобкой в ​​списке инициализаторов и поэтому не должна быть просто назначена вызовом ::CocreateGuid() в теле конструктора.

Есть ли более простой способ инициализировать это выражение лямбда?

Ответ 1

Когда выражение лямбда правильное, я использовал бы для этого вспомогательную функцию:

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

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

Кроме того, create_guid() имеет значение само по себе и может быть повторно использован (даже если сделать его деталью реализации возможно/правильно).

Ответ 2

Вы должны рассмотреть возможность переноса GUID в свой класс:

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;
};

Теперь вы можете использовать вышеуказанное в качестве участника:

class CSomeClass
{
    const CGUID m_guid;
...

Ответ 3

Ваше предложение - самый простой способ инициализировать постоянный член экземпляра.

Не бойтесь лямбда, на самом деле, в общем, это новая рекомендация стиля использовать lambdas для сложных инициализаций констант и ссылок, поскольку они разделяют свойство только инициализируется в точке объявления ( или инициализации члена экземпляра в списке инициализаторов).

Кроме того, ваш код запускает "именованную оптимизацию возвращаемого значения", и при возврате из лямбда нет конструкции копирования.

Интерфейс CoCreateGuid недостаточен, поскольку для него требуется выходной аргумент.

Если вы настаиваете на том, чтобы не использовать лямбда, я думаю, что следующая наиболее практичная альтернатива заключается в том, чтобы в теле конструктора деконфинировать с помощью const_cast передать его в CoCreateGuid.

Имейте в виду, что тот, который вы вводите в тело конструктора, язык считает, что все отдельные члены были правильно инициализированы, и вызовет для них деструкторы, если произойдет исключение, это имеет большое значение: инициализируется ли что-то в инициализаторе список или слева с двоичным рисунком мусора.

Наконец, к сожалению, вы не можете просто вызвать CoCreateGuid с деконфигурированной ссылкой на m_guid в лямбда, потому что лямбда все равно вернет значение и перезапишет элемент. Это по сути то же, что и вы уже писали (за исключением конструктора по умолчанию g)

Ответ 4

Здесь мы абстрагируем ваш шаблон:

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)
  )
{}

где мы обнаруживаем отказ и утверждаем, а затем бросаем, если это так.

Я не уверен, что это проще.

Действительно, напишите функцию GUID make_guid(), вставьте ее в некоторый заголовок и вызовите ее.

Ответ 5

Было бы проще, если бы объявить m_guid как экземпляр mutable, а не const. Разница в том, что mutable похожи на const для пользователей класса, но отлично класс lvalue внутри класса