1

I came across these lines of code:

template< int I > struct arg
{
    arg()
    {
    }

    template< class T > arg( T const & /* t */ )
    {
        // static assert I == is_placeholder<T>::value
        typedef char T_must_be_placeholder[ I == is_placeholder<T>::value? 1: -1 ];
    }
};

template< class T > struct is_placeholder
{
    enum _vt { value = 0 };
};

What could be the reason the struct is_placeholder is templated while typename T is not used anywhere inside?

Why T_must_be_placeholder is defined in such a way so that it can have invalid size -1. To realise this, I called arg<1>(1) and it gave error: size of array is negative as expected. Is it some sort of sanity-check technique? Why doesn't the compiler report this issue if the arg<1>(1) call is not made?

While

int i = 0;
char a[i == 1 ? 1 : -1]; //No error

If sanity check works for 1st example, then how does it fail for second one?

Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79
  • This is a poor man's implementation of static_assert. If some condition is not met, an array of negative size is declared, which triggers a compilation error. – n. m. could be an AI Oct 18 '16 at 13:26
  • Your last example should not compile, because the size of the array is not a constant expression. It probably does because of a GCC extension, not sure what happens if you pass a negative size. – sbabbi Oct 18 '16 at 13:46
  • Surprisingly, this does compile http://ideone.com/LrWvHh. Check out. – Saurav Sahu Oct 18 '16 at 13:47

1 Answers1

0

is_placeholder is a template so that it can be specialized for different T. Say I create a struct example and want is_placeholder<example>::value to be 42. I would do this:

struct example {};

template< class T > struct is_placeholder
{
    enum _vt { value = 0 };
};

template<>
struct is_placeholder<example>
{
    enum _vt { value = 42 };
};

template< int I > struct arg
{
    arg()
    {
    }

    template< class T > arg( T const & /* t */ )
    {
        // static assert I == is_placeholder<T>::value
        typedef char T_must_be_placeholder[ I == is_placeholder<T>::value ? 1: -1 ];
    }
};

void test()
{
    auto x = arg<42>(example()); // compiles
    auto y = arg<43>(example()); // assertion
    return 0;
}

In this case T_must_be_placeholder checks if is_placeholder<example>::value (which we just specialized to be 42) is the same as I. So if I is 42 it compiles, but if I is anything else, it won't.

Attempting to create an array with negative size is a way of deliberately preventing the code from compiling, so yes, this is probably a sanity check (static assertion, as it says in the comment).

  • About why `is_placeholder` is templated? Sorry I am little inexperienced in using templates. :| – Saurav Sahu Oct 18 '16 at 13:30
  • Why does not `int i = 0; char a[i == 1 ? 1 : -1];` fail then? – Saurav Sahu Oct 18 '16 at 13:46
  • @SauravSahu because then you have VLA (a gcc extension for arrays with size known at runtime). make i const and you get an error – bolov Oct 18 '16 at 13:54
  • I suspect because `i` isn't statically known that this causes a variable size array to be allocated on the stack at runtime rather than a compilation error. See http://stackoverflow.com/questions/737240/c-c-array-size-at-run-time-w-o-dynamic-allocation-is-allowed –  Oct 18 '16 at 13:55
  • @user634175 your suspicion is correct, if by "statically known" you meant "known at compile time" :p – bolov Oct 18 '16 at 13:57
  • @bolov indeed I did, may the pedantry police take me away :) -- incidentally `constexpr int i = 0; char a[i == 1 ? 1 : -1];` works! –  Oct 18 '16 at 13:59
  • @user634175 Already called them. Fortunately for you they don't take my calls anymore... – bolov Oct 18 '16 at 14:00
  • @user634175 `const int i = 0` is enough for it to work – bolov Oct 18 '16 at 14:02