在使用输出参数函数初始化 const 成员的构造函数中调用的代码比 lambda 更简单
Code simpler than lambda for a call in constructor that uses an output parameter function for initializing a const member
在header中,我有
class CSomeClass
{
const GUID m_guid;
public:
CSomeClass();
///...
}
并且在源文件中
CSomeClass::CSomeClass()
, m_guid(
[]() {
GUID g;
::CoCreateGuid(&g);
return g;
}()
)
{
}
如您所知,Guid 可用作不打算更改的标识。鉴于 ::CocreateGuid()
函数提供了我想要的输出参数,而不是返回它,我不能直接使用对函数的简单调用来初始化 m_guid 成员字段,即常量。
因此,它的常量性的结果是它必须在初始化列表中的左括号之前初始化,因此不能简单地通过在构造函数中调用 ::CocreateGuid()
来分配 body .
有没有比这个 lambda 表达式更简单的初始化方法?
你的建议是初始化常量实例成员的最简单方法。
不要害怕 lambda,事实上,一般来说,使用 lambda 进行常量和引用的复杂初始化是一种新的风格推荐,因为它们共享 属性 只被初始化在声明点(或初始化列表中的实例成员初始化)。
此外,您的代码触发了 "named return value optimization" 并且在 return 处没有来自 lambda 的复制构造。
CoCreateGuid 的接口有缺陷,因为它需要一个输出参数。
如果您坚持不使用 lambda,我认为下一个最实用的替代方法是在构造函数主体中使用 const_cast
将其传递给 CoCreateGuid。
请注意,当您进入构造函数的主体时,该语言认为所有单独的成员都已正确初始化,并且会在发生异常时为它们调用析构函数,这对于是否在其中初始化某些内容有很大的不同初始化列表或留下垃圾的二进制模式。
最后,不幸的是,您不能仅在 lambda 中使用对 m_guid
的非一致性引用来调用 CoCreateGuid,因为 lambda 仍将 return 一个值,这将覆盖该成员。它与您已经编写的内容基本相同(g
的默认构造函数除外)
这里我们抽象你的模式:
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()
函数,把它粘在一些header里,然后调用它。
当 lambda 表达式正确时,我会为此使用辅助函数:
GUID create_guid()
{
GUID g;
::CoCreateGuid(&g);
return g;
}
CSomeClass::CSomeClass() : m_guid(create_guid()) {}
此外,create_guid()
本身具有意义并且可以重复使用(即使将其作为实现细节是 possible/correct)。
如果将 m_guid
声明为 mutable
实例成员而不是 const
会更简单。不同之处在于 mutable
对于 class 的用户来说就像 const
,但在 class
中是一个完美的左值
您应该考虑将 GUID 包装在它自己的 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;
};
现在您可以使用上面的会员:
class CSomeClass
{
const CGUID m_guid;
...
在header中,我有
class CSomeClass
{
const GUID m_guid;
public:
CSomeClass();
///...
}
并且在源文件中
CSomeClass::CSomeClass()
, m_guid(
[]() {
GUID g;
::CoCreateGuid(&g);
return g;
}()
)
{
}
如您所知,Guid 可用作不打算更改的标识。鉴于 ::CocreateGuid()
函数提供了我想要的输出参数,而不是返回它,我不能直接使用对函数的简单调用来初始化 m_guid 成员字段,即常量。
因此,它的常量性的结果是它必须在初始化列表中的左括号之前初始化,因此不能简单地通过在构造函数中调用 ::CocreateGuid()
来分配 body .
有没有比这个 lambda 表达式更简单的初始化方法?
你的建议是初始化常量实例成员的最简单方法。
不要害怕 lambda,事实上,一般来说,使用 lambda 进行常量和引用的复杂初始化是一种新的风格推荐,因为它们共享 属性 只被初始化在声明点(或初始化列表中的实例成员初始化)。
此外,您的代码触发了 "named return value optimization" 并且在 return 处没有来自 lambda 的复制构造。
CoCreateGuid 的接口有缺陷,因为它需要一个输出参数。
如果您坚持不使用 lambda,我认为下一个最实用的替代方法是在构造函数主体中使用 const_cast
将其传递给 CoCreateGuid。
请注意,当您进入构造函数的主体时,该语言认为所有单独的成员都已正确初始化,并且会在发生异常时为它们调用析构函数,这对于是否在其中初始化某些内容有很大的不同初始化列表或留下垃圾的二进制模式。
最后,不幸的是,您不能仅在 lambda 中使用对 m_guid
的非一致性引用来调用 CoCreateGuid,因为 lambda 仍将 return 一个值,这将覆盖该成员。它与您已经编写的内容基本相同(g
的默认构造函数除外)
这里我们抽象你的模式:
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()
函数,把它粘在一些header里,然后调用它。
当 lambda 表达式正确时,我会为此使用辅助函数:
GUID create_guid()
{
GUID g;
::CoCreateGuid(&g);
return g;
}
CSomeClass::CSomeClass() : m_guid(create_guid()) {}
此外,create_guid()
本身具有意义并且可以重复使用(即使将其作为实现细节是 possible/correct)。
如果将 m_guid
声明为 mutable
实例成员而不是 const
会更简单。不同之处在于 mutable
对于 class 的用户来说就像 const
,但在 class
您应该考虑将 GUID 包装在它自己的 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;
};
现在您可以使用上面的会员:
class CSomeClass
{
const CGUID m_guid;
...