19

I need to make sure a template struct is exactly the size of its members. static_assert seems to be the tool of choice here. However, I cannot use a static_assert inside the struct itself, because the size is not yet known there. Here is what I would like to have:

template<typename T1,typename T2>
struct foo {
    T1 v1;
    T2 v2;
    // Doesn't compile, invalid application of sizeof to incomplete type
    static_assert(sizeof(foo<T1,T2>)==sizeof(T1)+sizeof(T2),"Struct size invalid");
};

This doesn't work. So how to do it? I don't want to burden people that instantiate the template to check themselves in each instantiation. The check should be fully automatic whenever the struct is instantiated.

gexicide
  • 38,535
  • 21
  • 92
  • 152

3 Answers3

15

Rename foo to foo_t.

template<class A, class B>
struct foo_helper {
  using type=foo_t<A,B>;
  static_assert(sizeof(A)+sizeof(B) == sizeof(type), "ouch");
};
template<class A, class B>
using foo=typename foo_helper<A,B>::type;

Note that using foo instead of foo_t blocks deduction in some cases, so this technique is not perfect.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
9

You may add a layer:

template<typename T1,typename T2>
struct bar {
    T1 v1;
    T2 v2;
};

template<typename T1,typename T2>
struct foo : bar<T1, T2> {
    static_assert(sizeof(bar<T1, T2>) == sizeof(T1) + sizeof(T2), "Struct size invalid");
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
6

Place the static assertion in a member function.

template<typename T1,typename T2>
struct foo {
    T1 v1;
    T2 v2;

 static auto assertion()
 {
    static_assert(sizeof(foo<T1,T2>)==sizeof(T1)+sizeof(T2),"Struct size invalid");
 }
};

This works because the contents of member functions are not compiled until the class has a complete definition.

There is no need to call the function.

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • I am afraid, but your answer doesn't work. At least with my compiler (usual ubuntu gcc). I tried inserting a false assert into the method (I inserted the condition and another static_assert with the negated condition, so one of both must be false) and no assertion triggered. – gexicide Feb 03 '17 at 13:36
  • 3
    _"`[C++14: 14.7.1/1]:`_ [..] _**The implicit instantiation of a class template specialization causes the implicit instantiation of the declarations, but not of the definitions**, default arguments, or exception-specifications **of the class member functions**, member classes, scoped member enumerations, static data members and member templates;_ [..] _"_ – Lightness Races in Orbit Feb 03 '17 at 14:09
  • 3
    _"`[C++14: 14.7.1/2]:` Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, **the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist**;_ [..] _"_ – Lightness Races in Orbit Feb 03 '17 at 14:10
  • 2
    So, yes, you'd have to call it. – Lightness Races in Orbit Feb 03 '17 at 14:11
  • 1
    However, [it works for me](http://coliru.stacked-crooked.com/a/97add29c8610d888) lol wtf – Lightness Races in Orbit Feb 03 '17 at 14:13
  • 2
    @LightnessRacesinOrbit this looks like a cue for a new "who's compiler is wrong" question :) – Richard Hodges Feb 03 '17 at 14:16
  • 2
    @LightnessRacesinOrbit its because your assert not depedent on template types. it's allowed to both fire and not assertion – RiaD Feb 03 '17 at 15:49
  • 1
    replace it with something like sizeof(A) == sizeof(A) + 1 and it will not fire – RiaD Feb 03 '17 at 15:50
  • http://coliru.stacked-crooked.com/a/8056aa60c96ea0d0 (as far as I understand it's still allowed to fire but compiler wouldn't know that) – RiaD Feb 03 '17 at 15:51
  • 2
    @RiaD: Hm. Okay but how does that tally with 14.7.1/1 cited above – Lightness Races in Orbit Feb 03 '17 at 16:13
  • 1
    @LightnessRacesinOrbit ¯\\_(ツ)_/¯ – RiaD Feb 03 '17 at 16:18
  • 2
    The rules about template functions "must have a valid instantiation" (if we presume members of template classes are template functions in this context) would make the `static_assert(false, "hello")` be ill-formed, no diagnostic **required**. So both compilers would be correct. – Yakk - Adam Nevraumont Feb 03 '17 at 17:52
  • @LightnessRacesinOrbit `static_assert(false, "!");` will basically always trigger everywhere, since it's non-dependent and it can. Which is annoying, since you have to write `template struct always_false : std::_false_type { }; static_assert(always_false::value, "!");`. Sigh. – Barry Feb 03 '17 at 18:43
  • @Barry Is the legality of a `static_assert` is anywhere dependent on it being in a dependent context in the standard? I suspect your hack is making an ill formed program with no diagnostic required rather than actually creating a truely "only static assert when instantiated and always false" function. But that section of the standard needs clarification (is a non-template function of a template class a template function with regards to the clause in question, or not?) – Yakk - Adam Nevraumont Feb 03 '17 at 18:46
  • @Yakk ¯\_(ツ)_/¯ – Barry Feb 03 '17 at 18:47