Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a user-defined type initialize *exactly* like a built-in type?

I would like to make a type that wraps a numeric type (and provides additional functionality).
Furthermore, I need the number and the wrapper to be both implicitly convertible to each other.

So far I have:

template<class T>
struct Wrapper
{
    T value;
    Wrapper() { }
    Wrapper(T const &value) : value(value) { }
    // ... operators defined here ...
};

It's almost good, but it doesn't quite behave the same as a built-in type:

#include <iostream>

int main()
{
    unsigned int x1, x2 = unsigned int();
    Wrapper<unsigned int> y1, y2 = Wrapper<unsigned int>();

    std::cerr << x1       << std::endl;  // uninitialized, as expected
    std::cerr << y1.value << std::endl;  // uninitialized, as expected

    std::cerr << x2       << std::endl;  // zero-initialized, as expected
    std::cerr << y2.value << std::endl;  // uninitialized!?!
}

Is there any way for me to design the Wrapper such that statements like

Wrapper<unsigned int> y2 = Wrapper<unsigned int>();

initialize the value inside, but without forcing statements like

Wrapper<unsigned int> y1;

to also do the same?

In other words, is it possible to make a type that behaves exactly the same as a built-in type in terms of initialization?

like image 836
user541686 Avatar asked Mar 08 '14 04:03

user541686


People also ask

What is a user-defined type c++?

User Defined Data type in c++ is a type by which the data can be represented. The type of data will inform the interpreter how the programmer will use the data. A data type can be pre-defined or user-defined. Examples of pre-defined data types are char, int, float, etc.

What is a user-defined type?

A user-defined data type (UDT) is a data type that derived from an existing data type. You can use UDTs to extend the built-in types already available and create your own customized data types. There are six user-defined types: Distinct type. Structured type.

Which one is not a user-defined type in C#?

So, clearly long int l = 2.35; is not User-defined data type.

Which of the following is referred to as user-defined data types as they are not pre-defined by the C++ compiler?

Non-primitive data type – These data types are composed of primitive data types. These are also referred to as user-defined data types as they are not pre-defined by the C++ compiler. For example arrays, structures, unions, class, linked lists, enumeration, etc.


2 Answers

Updated Answer

Okay, so as dyp points out, I and everyone else was wrong. You can achieve what you want to do by = default with the default constructor:

 Wrapper() = default ;
           ^^^^^^^^^

This works because without an initializer you obtain the same behavior I outline before but when you use value initialization the behavior changes as outlined in paragraph 8:

— if T is a (possibly cv-qualified) non-union class type without a user-provided or deleted default constructor, then the object is zero-initialized and, if T has a non-trivial default constructor, default-initialized;

Original Answer

I don't think there is a way to make this work the way you would like. Class types act differently that builtin types we can see this from the draft standard section 8.5 Initializers paragraph 12 which says (emphasis mine going forward):

If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an object with automatic or dynamic storage duration has indeterminate value. [ Note: Objects with static or thread storage duration are zero-initialized, see 3.6.2. —end note ]

and we can see this has different results for classes than built-in types from paragraph 7 which says:

To default-initialize an object of type T means:

and includes the following bullets:

— if T is a (possibly cv-qualified) class type (Clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);

— if T is an array type, each element is default-initialized;

otherwise, no initialization is performed.

and if we look at paragraph 11 for the second case Wrapper<unsigned int>() it says:

An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.

and then back to paragraph 8:

To value-initialize an object of type T means:

— if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is user-provided or deleted, then the object is default-initialized; [...]

So we end up with the same behavior.

Both Praetorian and aschepler gave you options that work slightly differently but appear to achieve the behavior you would like just not with the same syntax.

like image 171
Shafik Yaghmour Avatar answered Sep 28 '22 16:09

Shafik Yaghmour


I don't think there's any way to achieve what you're looking for. As soon as you define a default constructor for a class that will be called whether you provide or omit parentheses when defining an instance of the class.

You can get kinda close by declaring the following constructor; the variable definition will require an empty pair of braces to achieve value initialization.

Wrapper(std::initializer_list<std::initializer_list<T>> /*unused*/) : value() {}

auto y3 = Wrapper<unsigned int>({}); // y3.value will be value initialized

Live demo

But I'd sooner drop the requirement for implicit conversion to Wrapper, and keep the class an aggregate, than implement the solution above.

like image 44
Praetorian Avatar answered Sep 28 '22 16:09

Praetorian