让我们考虑一下定义:
struct ClassWithMember
{
int myIntMember = 10;
}
我想获取
myIntMember
的默认值,但不要创建该类的另一个实例
// IMPOSSIBLE int myInt = ClassWithMember::myIntMember;
// MUST AVOID int myInt = ClassWithMember().myIntMember;
我知道解决方法,但不喜欢它:
struct ClassWithMember
{
static const int myIntMember_DEFAULT = 10;
int myIntMember = myIntMember_DEFAULT;
}
int myInt = ClassWithMember::myIntMember_DEFAULT;
因为需要额外的线路。而且我无法定义像
static const int *const INTEGER11_DEFAULT = 0x100088855;
这样的内联静态指针,此类指针必须在.cpp
文件中定义,.hpp
中只是声明。我的许多类都是仅标头的,因此为此值创建多余的 .cpp
并不是一个好主意。
这里是C#的类似问题
我想,对你来说,这只是另一种解决方法,但我认为有点(只是一点点)更实用。
如果将默认值保存为静态方法返回的静态常量,则可以避免在 cpp 文件中添加额外的行。
下面的示例在模板包装器中实现了这一点(使用默认值作为带有默认值的模板参数;只是为了好玩),但模板部分只是为了避免示例中代码的重复
#include <iostream>
template <typename T, T defTpl = T{}>
struct wrapperWithDef
{
static T getDefVal ()
{ static T const def { defTpl }; return def; }
T myTMember { getDefVal() };
};
int main()
{
wrapperWithDef<int> wi;
wrapperWithDef<long, 3L> wl;
wrapperWithDef<int *> wp;
// print "0, 3, (nil)" (clang++) or "0, 3, 0" (g++)
std::cout << wi.myTMember << ", " << wl.myTMember << ", "
<< wp.myTMember << std::endl;
// print "5, (nil), 1" (clang++) or "5, 0, 1" (g++)
std::cout << wrapperWithDef<unsigned, 5U>::getDefVal() << ", "
<< wrapperWithDef<long *>::getDefVal() << ", "
<< wrapperWithDef<bool, true>::getDefVal() << std::endl;
return 0;
}
我称此解决方案为解决方法
struct ClassWithMember
{
static const int myIntMember_DEFAULT = 10;
int myIntMember = myIntMember_DEFAULT;
}
int myInt = ClassWithMember::myIntMember_DEFAULT;
对于指针来说会看起来更复杂
//.hpp
struct ClassWithMember
{
static AnotherClass* const myMember_DEFAULT; //=X; Assignment not allowed
AnotherClass* myMember = myMember_DEFAULT;
}
//.cpp
AnotherClass* const MyNamespace::ClassWithMember::myMember_DEFAULT = pAnotherInstance;
//usage
auto *my = ClassWithMember::myMember_DEFAULT;
如果您有能力拥有
constexpr
友好的结构,请考虑以下想法。 consteval
可用于在编译时强制对象初始化(和销毁)。std::string
为 std::string_view
设置默认值的方法 (C++20 起):https://godbolt.org/z/jxh1s8qev
#include <type_traits>
#include <array>
#include <string>
template <typename T, typename V>
consteval V _mem_type(V T::*) noexcept;
template <auto M, typename = decltype(_mem_type(M))>
struct default_value;
template <typename T, typename V, V T::*M>
struct default_value<M, std::enable_if_t<std::is_fundamental_v<V>, V>>
{
consteval V operator()() const {
return T{}.*M;
}
};
// This is only possible starting from C++20
template <typename T, std::string T::*M>
struct default_value<M, std::string>
{
private:
constexpr static auto _v =
[]
{
std::array<char, (T{}.*M).size()> v{};
auto i = v.begin();
for (char c: T{}.*M)
*i++ = c;
return v;
}();
public:
consteval std::string_view operator()() const {
return {&_v[0], _v.size()};
}
};
template <auto M>
constexpr inline default_value<M> default_value_v{};
// Example
//////////
#include <iostream>
struct foo
{
int i = 815;
std::string s = "oceanic";
};
int main() {
constexpr int iDefault = default_value_v<&foo::i>();
std::cout << "default foo::i = " << iDefault << '\n';
constexpr auto sDefault = default_value_v<&foo::s>();
std::cout << "default foo::s = " << sDefault << '\n';
}
输出:
default foo::i = 815
default foo::s = oceanic
基本上,必须解决使用动态分配的类型的内容返回问题。
显然,它不适用于非
constexpr
友好的类型,因为不允许从 constexpr 上下文返回动态分配的资源。