Asked  7 Months ago    Answers:  5   Viewed   25 times

In-class initializers (C++11 feature) must be enclosed in curly braces or follow a = sign. They may not be specified inside parenthesis.

What is the reason for this?

 Answers

87

I am not 100% positive about this, but this might be to prevent a syntax ambiguity. For example, consider the following class:

class BadTimes {
    struct Overloaded;
    int Overloaded;            // Legal, but a very strange idea.

    int confusing(Overloaded); // <-- This line
};

What does the indicated line mean? As written, this is a declaration of a member function named confusing that accepts as a parameter an object of type Overloaded (whose name isn't specified in the function declaration) and returns an int. If C++11 were to allow initializers to use parentheses, this would be ambiguous, because it could also be a definition of a member of type int named confusing that is initialized to the value of the data member Overloaded. (This is related to the current issue with the Most Vexing Parse.)

By requiring curly braces, this ambiguity is removed:

class BadTimes {
    struct Overloaded;
    int Overloaded;            // Legal, but a very strange idea.

    int confusing{Overloaded}; // <-- This line
};

Now, it's clear that confusing is actually an int initialized to the value of Overloaded, because there's no way to read it as a function declaration.

Hope this helps!

Tuesday, June 1, 2021
 
CAMason
answered 7 Months ago
18

You can't instantiate an interface or an abstract class because it would defy the object oriented model.

Interfaces represent contracts - the promise that the implementer of an interface will be able to do all these things, fulfill the contract.

Abstract classes are a similar idea, in that they represent an unfulfilled contract, a promise to be able to do things, except unlike interfaces they have some of their functions or fields defined but need filling in before they can used.

Simply, in a good object oriented program, you should never want to instantiate an abstract class or interface. If you do, the design is probably wrong.

(Anonymous classes are actually non-abstract instantiations, just that they don't need to be given a name, so they appear to be 'raw interfaces' but they're actually an implementation of the interface that has no name. That's my understanding, at least.)

Thursday, July 29, 2021
 
Whakkee
answered 5 Months ago
75

There is a recent proposal for movable initializer lists, where, in particular, the authors say:

std::initializer_list was designed around 2005 (N1890) to 2007 (N2215), before move semantics matured, around 2009. At the time, it was not anticipated that copy semantics would be insufficient or even suboptimal for common value-like classes. There was a 2008 proposal N2801 Initializer lists and move semantics but C++0x was already felt to be slipping at that time, and by 2011 the case had gone cold.

Sunday, August 15, 2021
 
davidb
answered 4 Months ago
89
 auto ar = wrap({a,b,c}); 

This create a temporary array of type int[3], then binds an initializer_list<int> to that array, then calls wrap which creates an array<const int> that refers to the array.

At the end of the expression the array is destroyed, leaving the array<const int> with a dangling pointer, so this is undefined behaviour:

 std::cout<< ar[2] << std::endl;

This also applies to the code in main, the variable a contains a dangling pointer and a[2] is undefined behaviour.

You can verify this by replacing the array of int with an array of types that allocate memory, so that valgrind or asan will notice the bug:

using V = std::vector<int>;
auto a = wrap({V{1}, V{2}, V{3}});
std::cout<< a[2].front() << std::endl;

Now a[2] is a std::vector<int> object, but trying to access its front() member causes the program to abort:

==28356==ERROR: AddressSanitizer: heap-use-after-free on address 0x60200000efb0 at pc 0x000000401205 bp 0x7fffa46f2900 sp 0x7fffa46f28f8
READ of size 4 at 0x60200000efb0 thread T0
    #0 0x401204 in main /tmp/il.cc:28
    #1 0x3236e21d64 in __libc_start_main (/lib64/libc.so.6+0x3236e21d64)
    #2 0x400ec8  (/tmp/a.out+0x400ec8)
...

Or with valgrind:

==28364== Invalid read of size 4
==28364==    at 0x400C72: main (il.cc:28)
==28364==  Address 0x51dfd20 is 0 bytes inside a block of size 4 free'd
==28364==    at 0x4A07991: operator delete(void*) (vg_replace_malloc.c:502)
==28364==    by 0x4013BF: __gnu_cxx::new_allocator<int>::deallocate(int*, unsigned long) (new_allocator.h:110)
==28364==    by 0x4012F8: std::allocator_traits<std::allocator<int> >::deallocate(std::allocator<int>&, int*, unsigned long) (alloc_traits.h:386)
==28364==    by 0x4011B1: std::_Vector_base<int, std::allocator<int> >::_M_deallocate(int*, unsigned long) (stl_vector.h:178)
==28364==    by 0x40102A: std::_Vector_base<int, std::allocator<int> >::~_Vector_base() (stl_vector.h:160)
==28364==    by 0x400EC4: std::vector<int, std::allocator<int> >::~vector() (stl_vector.h:425)
==28364==    by 0x400C2A: main (il.cc:27)

Side question; if I tried to return my wrapped array in test, the initializer list {a,b,c} would get out of scope, and the array I'm returning would be invalid -- is that correct?

It's already out of scope and ar already invalid even before you return it.

Monday, August 30, 2021
 
Ujjawal Khare
answered 4 Months ago
87

That's the feature. Scoped enumerations are not implicitly convertible to integers, so you can't use them in lieu of integers. They are strongly typed by design. If you want the implicit conversion, use unscoped enumerations.

enum MsgType : int {
  Output = 1,
  Input  = 2,
  Debug  = 3
};
enum MsgSender : int {
  A = 1,
  B = 2,
  C = 3
};

Scoping and the specification of an underlying type are orthogonal.

Alternatively, if you only want some operations to be defined, while the enumerations remain strongly typed in general, you can overload the appropriate operators to accomplish that

int operator<<(MsgType, int); // etc

But if I would not want them to be convertible, why would i specify the type to be int

To ensure a certain layout. To follow a specific ABI. To allow forward declaring the type.

Tuesday, August 31, 2021
 
Indranil
answered 4 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share