0

I have some templated code for timers abstraction on my avr device. Most relevant part here is:

template <typename T> class Timerx8bit { T reg; static_assert(T::res == TimerResolution::bit8, "The timer template is 8bit, registers must also be 8bit"); } struct Timer0 { constexpr static const TimerResolution res = TimerResolution::bit16; volatile uint8_t* tccra = &TCCR0A; volatile uint8_t* tccrb = &TCCR0B; //[...] }; 

Now I feed the Timer0 to the template. The problem with that code is, that the static assert seems to evaluate always to true, although in the above situation it should fail. TimerResolution is just an enum class.

The problem seems to be in the template, if I put "TimerResolution::bit8 != TimerResolution::bit8" as the condition, compilation fails as expected, but "T::res != T::res" compiles without any problems... what am I missing here?

EDIT: While preparing full example of code I found the problem, although I still don't quite understand why it behaves that way. First, the code:

enum class TimerResolution_test { bit8, bit16 }; struct Timer0_test { constexpr static const TimerResolution_test res = TimerResolution_test::bit8; }; template <typename T> class Timerx8bit_test { public: constexpr static const TimerResolution_test res = TimerResolution_test::bit8; private: T reg; static_assert(T::res != T::res, "The timer template is 8bit, registers must also be 8bit"); }; template<typename Timer> class pwm_test { }; 

Instantiation:

pwm_test<Timerx8bit_test<Timer0_test>> testTimer; // Compiles Timerx8bit_test<Timer0_test> testTimer2; // Fails 

The second instantiation fails with the static_assert as above. If I put 'false' instead of the templated condition it fails in both cases... why is that? Shouldn't it fail in both cases with the original templated condition?

3
  • 3
    What is TimerResolution? What is TimerResolution::bit8? If possible please try to create a Minimal, Complete, and Verifiable Example and show us. Commented Oct 31, 2016 at 14:35
  • 1
    Cannot reproduce. I put your code in a single cpp file and when it is compiled, the static_assert did fail. Commented Oct 31, 2016 at 14:41
  • Three compilers agree, so it's probably not a compiler bug. Bilut I have a hard time imagining what else it could be. Commented Oct 31, 2016 at 15:41

1 Answer 1

1

Templates don't require the complete type definition immediately (think CRTP). They must use the type in a way that will cause the complete type definition to be required. Your pwm_test doesn't use the type parameter it is given except by name. So the template body never needs to be instantiated.

In the second case you create an object, so naturally the template body instantiation happens.

So you need to force the instantiation by providing a context where it must happen:

template<typename Timer> class pwm_test { enum { _ = sizeof(Timer) }; }; 

On a related note, if you have any static assertions in a member function of a template, those won't be triggered until you add a call to the function in your code.


To answer your other question, why does static_assert result in an immediate error in one case but not the other:

§14.6/8 [temp.res]

Knowing which names are type names allows the syntax of every template to be checked. The program is ill-formed, no diagnostic required, if:

  • ...
  • a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or

So when encountering static_assert(false) while parsing the template, the compiler can deduce that all templates instantiations will be ill-formed. In this case it chooses to issue a diagnostic immediately (note that it doesn't have to).

When static_assert(T::res != T::res) is encountered, the compiler checks the syntax, but it cannot deduce that T::res != T::res is always false, that information is only available when T is known (after all, T::res can be something that overloads operator!= to always return true).

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for the suggestion. In the finished code the pwm class will have a member of Timer, so there is really no problem anymore - I just would love to understand, why it's not instantiated for the sake of checking the compile condition...
@StorryTeller, yes, but only partly. The assert is in the class body, so no member call is needed. I understand, that the template body doesn't have to be instantiated - but if it's not, then why putting 'false' in the static_assert fails? If it's not instantiated, shouldn't it be left without processing?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.