Код проще, чем 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 внутри класса