3

I am trying to use a pre-C++11 static assert. I found this and this question, but somehow I cant get it running:

#define STATIC_ASSERT(x) \
    do { \
        const static char dummy[(x)?1:-1] = {0};\
    } while(0)

struct bar {
    int value;
    template<typename T> void setValue(T x);
};
template<typename T> void bar::setValue(T x) { STATIC_ASSERT(1==0); }
template<> void bar::setValue(int x) { value = x;}

int main(){
    bar b;
    int c = 1;
    b.setValue(c);    
}

Compiling this (gcc) results in

error: size of array 'dummy' is negative

I would expect this error to apprear only if I call setValue with anything other than int. I also tried other proposed solutions, but with more or less the same result: The error is there even if I dont instantiate the template with anything other than int. What am I doing wrong?

Community
  • 1
  • 1
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Just curious, you know you don't have to use template at all in such case, and simply write it with `int` type? (It's still interesting question and answer for educational reasons, but I hope your real usage is not as trivial :) ). – Ped7g Oct 12 '16 at 09:58
  • @Ped7g well, in fact my real usage is as trivial as the example. I want to make absolutely sure that the function is called with parameter of the correct type and no conversion takes place. Whether this is good approach or not I am not yet sure, but thats not a discussion for here. I will put it on codereview... – 463035818_is_not_an_ai Oct 12 '16 at 10:00
  • look up the explict keyword – UKMonkey Oct 12 '16 at 10:58
  • @UKMonkey as I understood `explicit` it allows me to avoid conversions of one particular type (whose code I wrote) but I dont see how this would help to avoid conversions taking place for the parameter of one particular function (e.g. `double` to `int`). Moreover `explicit` is C++11 but I am looking for a C++03 solution – 463035818_is_not_an_ai Oct 12 '16 at 13:42
  • explictit works in C98 for constructors; and would allow you to do something like template X { X(explicit T); &T operator (); } The joy of this is that compiler errors would then say it couldn't convert to if you did something wrong – UKMonkey Oct 12 '16 at 13:46
  • @UKMonkey sorry I dont see how this could help. My real code looks actually quite similar to the one posted here, ie a setter that should issue a compiler error for any other than the correct type. Do you maybe mean that my `setValue` should take a `X` as parameter? – 463035818_is_not_an_ai Oct 12 '16 at 13:51
  • @UKMonkey: What is "C98"?! – Kerrek SB Oct 12 '16 at 14:48

1 Answers1

7

If a template is invalid for every instantiation, then the program is ill-formed, no diagnostic required. GCC is therefore perfectly valid in giving you an error here, as the primary template for setValue is invalid no matter what the template argument is.

The way to solve this issue is to make the STATIC_ASSERT expression dependent on a template parameter. One option is to make a dependent_false template class, like this:

template <typename T> struct dependent_false 
{ const static bool value = false; };

template<typename T> void bar::setValue(T x) 
{ STATIC_ASSERT(dependent_false<T>::value); }
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • "If a template is invalid for every instantiation, then the program is ill-formed" even if there is a specialization that is not ill-formed? – 463035818_is_not_an_ai Oct 12 '16 at 09:53
  • ok, got it, but it feels a bit strange that your hack is working. If the compiler would be "clever enough" he could still realize that the template is illformed for any T – 463035818_is_not_an_ai Oct 12 '16 at 09:58
  • 1
    That's what two-phase lookup is about. Non-dependent calls are checked in phase 1 – krzaq Oct 12 '16 at 10:02
  • @tobi303 What if someone defines a specialization of `dependent_false` for some `T` and gives it a `... value = true;`? You don't know until instantiation time whether `value` is still `false`, or even if it exists at all. – TartanLlama Oct 12 '16 at 10:04
  • @TartanLlama well, now I am getting confused, what if someone defines a specialization of `setValue` for some `T` that is not ill-formed.... that also didnt work. I will have to do so more research to understand this. Thanks for the help – 463035818_is_not_an_ai Oct 12 '16 at 10:05
  • @tobi303 The difference is that the primary template for `setValue` is invalid no matter what happens, so the standard lets compilers fail-fast in order to diagnose what could well be bad code. Consider a library template class which is invalid regardless of the template arguments; it's very helpful for a compiler to be able to diagnose that without ever having to instantiate it. – TartanLlama Oct 12 '16 at 10:09
  • If at the end of the translation unit, no specialization of `dependent_false` exist that *could* possible make `value` true, your program is still *"ill-formed; NDR"*, although implementations don't go to that length in testing this. For maximal safety, you should add a specialization like `template<> struct dependent_false { static const bool value = true; }`. – Johannes Schaub - litb Oct 12 '16 at 10:11
  • I'm skimming the C++03 standard but I can't find the proof of this. – krzaq Oct 12 '16 at 10:14
  • @JohannesSchaub-litb I guess you could even do something like `template<> struct dependent_false { const static bool value = true; };` to avoid future issues. – TartanLlama Oct 12 '16 at 10:16