如何获取任何类型的默认值

How to get the default value of any type

本文关键字:任何 类型 默认值 获取 何获取      更新时间:2023-10-16

在C#中,我可以写这样的东西:

class AnyThing<T>
{
static public T Default = default(T);
}
static void Main ()
{
int i = AnyThing<int>.Default;
Console.WriteLine (i==0);
string s = AnyThing<string>.Default;
Console.WriteLine (s == null);
}

我打算在C++中编写一个类似模板类的字典,如果找不到给定的键,我希望字典返回通用 TVal 类型的默认值(零出)。在 C# 中,default(T) 构造可以提供帮助,而在 C++ 中,我不确定执行相同操作的合适方法是什么。

我已经尝试过T obj = {}并使用gcc4.7T* obj = {},它运行良好。我只是不太确定它是否是语言规范定义的语法,如果这种代码将是可移植的跨编译器和平台。请帮我做我的杜特!提前感谢!

附注:

~~~~~~~~~

为了确保模板获得 ANY 类型的默认(零输出)值,即使是那些没有可调用的默认 ctor 的值,我采用了以下机制(灵感来自 avakar 的答案):

template<class T>
struct AnyThing
{
static const T& Default ;
private:
static const char temp[sizeof(T)];
};
template<class T> const char AnyThing<T>::temp[] = {};
template<class T> const T& AnyThing<T>::Default =  *(T*)temp;
struct st
{
double data;
st()=delete;
};
int main()
{
cout << (int)AnyThing<char*>::Default<<endl;    //0
cout << AnyThing<int>::Default<<endl;       //0
cout <<AnyThing<st>::Default.data<<endl;        //0
}

它看起来很丑,但应该不会造成任何麻烦,毕竟归零的对象只是一大块空白内存。我错了吗?

在C++中,C# 中没有类似default关键字的东西。由于默认情况下初始化类类型的值构造函数将失败,如果默认构造函数为private.在 C# 中,如果默认构造函数是私有的,则类类型的值将被初始化为null,因为类类型是reference-type

{}初始化由语言规范定义。现在是C++11。在 C++03 中,您应该使用

T obj = T();

正如bames53在评论中指出的那样,当您要初始化T*时,您应该使用

C++11之前。

T* obj = 0;

T* obj = NULL;

在 C++11 中。

T* obj = {};

T* obj = nullptr;

字面意思摘自"The C++ Programming Language, Third Edition by Bjarne Stroustrup":

开始报价

4.9.5 初始化 [dcl.init]

如果为对象指定了初始值设定项,则该初始值设定项将确定对象的初始值。如果未指定初始值设定项,则全局 (§4.9.4)、命名空间 (§8.2) 或本地静态对象 (§7.1.2, §10.2.4)(统称为静态对象)将初始化为适当类型的 0。例如:

int a;  // means int a=0;
double d; // meands d=0;

默认情况下,局部变量(有时称为自动对象)和在自由存储中创建的对象(有时称为动态对象或堆对象)不初始化。例如:

void f()
{
int x;   // x does not have a well-defined value
// . . . 
}

数组和结构的成员是否默认初始化取决于数组或结构是静态的。用户定义类型可能定义了缺省初始化 (§10.4.2)。

更复杂的对象需要多个值作为初始值设定项。这是由 { 和 } 分隔的初始值设定项列表处理的,用于数组 (§5.2.1) 和结构 (§5.7) 的 C 样式初始化。

对于带有构造函数的用户定义类型,使用函数样式的参数列表 (§2.5.2、§10.2.3)。请注意,声明中的一对空括号 () 始终表示"函数"(§7.1)。例如:

int a[] = {1,2};    // array initializer
Point z(1,2);       // function-style initializer (initialization by constructor)
int f();            // function declaration

结束语

因此,您可以从该类型的静态对象获取任何类型的默认值:

static T defaultT; // `defaultT' has de default value of type T

创建自己的默认关键字:

class default_t
{
public:
template<typename T>
operator T() const { return T(); }
};
default_t const default = default_t();

像这样使用它:

int myInt = default;
vector<string> myVector = default;
shared_ptr<string> myPtr = default;

或者有轻微的语义变化:

default_t const empty = default_t();
vector<Persons> fetchPersons()
{
if (Database::isConnected())
{
return Database::fetchPersons();
}
return empty;
}

如果 ForEveR 没有复制构造函数,T的答案将不起作用。在 C++03 中,无法对既通用又优雅的变量进行零初始化。剩下的就是下面的技巧。

T temp[1] = {};
T & obj = temp[0];

在这里,temp[0]初始化为零,然后绑定到obj。不需要复制构造函数。

以下是我截至 C++20 的操作:

constexpr auto default_value(const auto& value) {
return std::decay_t<decltype(value)>{};
}
int main() {
static_assert(default_value(std::string_view{"foo"}) == std::string_view{});
static_assert(default_value(123) == 0);
static_assert(default_value(&main) == nullptr);
}

如果您已经拥有T{}std::decay_t<T>{}T的类型,如果您确定它不是引用/数组/...(因为这些没有默认值)。

在 C++ 11 上,我们可以使用type{}进行初始化 (https://en.cppreference.com/w/cpp/language/initialization)

所以:

int x{};//will work
decltype(x){};//as well as this

它也可以用于默认参数

template<typename Prev,typename Param,int n>
struct Bond {
const Prev& parent;
const Param param;
constexpr Bond(const Param param=Param{}):parent(Prev{}),param(param) {}
constexpr Bond(const Prev& parent,const Param param):parent(parent),param(param) {}
};