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

В шапке у меня есть

class CSomeClass
{
    const GUID m_guid;

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

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

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

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

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

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

5 ответов

Решение

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

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

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

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

Вы должны рассмотреть оборачивание 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;
...

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

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() функции, вставьте его в какой-то заголовок и вызовите его.

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

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

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

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

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

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

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

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

Другие вопросы по тегам