'How to get the default value of any type
In C# I can write something like this:
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);
}
I intend to write a dictionary like template class in C++, I'd like the dict to return the default value (zero out) of the generic TVal type if the given key not be found. In C# the default(T) construct comes to rescue, while in C++ I'm not sure what is the appropriate way to do the same thing.
I've tried T obj = {} and T* obj = {} with gcc4.7, it works well. I'm just not so sure if it is the syntax defined by the language specification, if this kinda code will be portable cross compilers and platforms. Please help me with my doudt! Thanks in advance!
PS:
~~~~~~~~~~
To make sure the template get the default(zero out) value of ANY type, even of those that don't have callable default ctor, I employed following mechanism (inspired by avakar's answer):
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
}
It looks ugly, but shouldn't cause any trouble, after all a zeroed out object is just a chunk of blank memory. Am I wrong?
Solution 1:[1]
Taken literaly from "The C++ Programming Language, Third Edition by Bjarne Stroustrup":
BEGIN QUOTE
4.9.5 Initialization [dcl.init]
If an initializer is specified for an object, that initializer determines the initial value of an object. If no initializer is specified, a global (§4.9.4), namespace (§8.2), or local static object (§7.1.2, §10.2.4) (collectively called static objects) is initialized to 0 of the appropriate type. For example:
int a; // means int a=0;
double d; // meands d=0;
Local variables (sometimes called automatic objects) and objects created on the free store (sometimes called dynamic objects or heap objects) are not initialized by default. For example:
void f()
{
int x; // x does not have a well-defined value
// . . .
}
Members of arrays and structures are default initialized or not depending on whether the array or structure is static. User-defined types may have default initialization defined (§10.4.2).
More complicated objects require more than one value as an initializer. This is handled by initializer lists delimited by { and } for C-style initialization of arrays (§5.2.1) and structures (§5.7).
For user-defined types with constructors, function-style argument lists are used (§2.5.2, §10.2.3). Note that an empty pair of parentheses () in a declaration always means ‘‘function’’ (§7.1). For example:
int a[] = {1,2}; // array initializer
Point z(1,2); // function-style initializer (initialization by constructor)
int f(); // function declaration
END QUOTE
So, you can get the default value of any type form a static object of that type:
static T defaultT; // `defaultT' has de default value of type T
Solution 2:[2]
Create your own default keyword:
class default_t
{
public:
template<typename T>
operator T() const { return T(); }
};
default_t const default = default_t();
Use it like:
int myInt = default;
vector<string> myVector = default;
shared_ptr<string> myPtr = default;
Or with a slight semantic variation:
default_t const empty = default_t();
vector<Persons> fetchPersons()
{
if (Database::isConnected())
{
return Database::fetchPersons();
}
return empty;
}
Solution 3:[3]
ForEveR's answer will not work if T doesn't have a copy constructor. In C++03, there is no way to zero-initialize a variable that is both generic and elegant. All that's left is the following trick.
T temp[1] = {};
T & obj = temp[0];
Here, temp[0] is zero-initialized and then bound to obj. No copy constructors are needed.
Solution 4:[4]
Here's how I'd do it as of 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);
}
If you already have the type T it'd just be std::decay_t<T>{} or T{} if you are sure that it is not a reference / array / ... (as those do not have a default value).
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 | avakar |
| Solution 4 | Jean-Michaël Celerier |
