1

Is there any way to make a type that has zero size and can only be constructed implicitly?

The use case is to prevent some public members of a struct from being initialized via the brace syntax:

class Barrier { ... };


struct Foo {
  int user_sets;
  int* this_to;

  Barrier _bar;

  int *must_be_zero_init_by_linker;
};

Foo foo = {1};               // ok
Foo bar = {1, nullptr};      // ok
Foo baz = {1, nullptr, {}};  // must error

Edit: one other constraint: The Foo object must be linker initialized so it can't define constructors or private members.

BCS
  • 75,627
  • 68
  • 187
  • 294
  • Does `class Barrier { Barrier() { } };` not work? – user541686 Mar 10 '14 at 21:08
  • I'm not sure what "can only be implicitly constructed" means. Do you want an error for `Barrier()`? That does rather explicitly construct a `Barrier` object. –  Mar 10 '14 at 21:10
  • 1
    Zero size? Do you mean a type `T` such that `sizeof(T) == 0`? That cannot happen according to the rules of C++. –  Mar 10 '14 at 21:10
  • @Mehrdad struct Barrier { Barrier() { } }; works fine for me. – camino Mar 10 '14 at 21:14
  • @Mehrdad I think that can't be implicitly constructed either. – BCS Mar 10 '14 at 21:18

2 Answers2

5

You could define your own constructor; this prevents your class from being an aggregate. For example:

struct Foo
{
  Foo(int a = 0, int * p = nullptr) constexpr
  : user_sets(a), this_to(p), must_be(nullptr)
  {}

  int user_sets;
  int* this_to;

  int *must_be;
};

Foo foo = { 1 };                 // ok
Foo bar = { 1, nullptr };        // ok
// Foo baz = { 1, nullptr, {} }; // error

In fact, I would recommend making the constructor explicit - then you can't use copy-initialization, but you can still use list-initialization:

explicit Foo(int a = 0, int * p = nullptr) constexpr /* ... */

Foo foo { 1 };                   // ok
Foo bar { 1, nullptr };          // ok
// Foo baz { 1, nullptr, {} };   // error
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • This is a much more idiomatic way to solve the problem. – djhaskin987 Mar 10 '14 at 21:18
  • explicit constructors are run at run-time, it needs to be at link time. That is the object needs to be valid at the point the main thread starts running. – BCS Mar 10 '14 at 21:19
  • 2
    No, the explicit constructor keyword prevents implicit construction at compile time. See this answer. http://stackoverflow.com/a/121163/850326 – djhaskin987 Mar 10 '14 at 21:24
  • 1
    @BCS: You can also make the constructor `constexpr` to make it eligible for the use in constant expressions. I'll edit that in. – Kerrek SB Mar 10 '14 at 21:30
0

Yes, an explicit default constructor will work:

struct Barrier { explicit constexpr Barrier() {} };

That gives the behaviour you want:

Foo foo = {1};               // ok
Foo bar = {1, nullptr};      // ok
Foo baz = {1, nullptr, {}};  // error

Note that the behaviour might change depending on the eventual resolution of DR 1518, so KerrekSB's answer is more reliable and less subtle.

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521