Is there a way to statically assert on indices known at compile-time and run-time assert otherwise? Example:
template <class T, int Dim>
class Foo
{
T _data[Dim];
public:
const T &operator[](int idx) const
{
static_assert(idx < Dim, "out of range"); // error C2131: expression did not evaluate to a constant
return _data[idx];
}
};
int main()
{
Foo<float, 2> foo;
foo[0];
foo[1];
foo[2]; // compiler error
for (int i=0; i<5; ++i)
{
foo[i]; // run time assert when i > 1
}
return 0;
}
I don't think it's possible obtain what do you want with a single function.
Even if you develop a constexpr
function, I don't think your are able to detect when is executed run-time and when executed compile time and act in a different way.
But you can develop different functions.
By example, a template get<>()
, where the template argument is the index, that can be used only with an index known at compile-time and that can perform a static_assert()
, and an at(std::size_t)
, that can receive an index computed at run time with a run time check.
En passant:
1) I suggest, as usual in STL, the use of at()
for a bound checked access and an operator[]()
for a bound unchecked access
2) and I suggest the use of an unsigned index or you have to check that the index is >= 0
.
The following is a working example
#include <iostream>
#include <stdexcept>
template <class T, std::size_t Dim>
class Foo
{
private:
T _data[Dim];
public:
T const & operator[] (std::size_t idx) const
{ return _data[idx]; }
template <std::size_t IDX>
T const & get () const
{
static_assert(IDX < Dim, "out of range");
return this->operator[](IDX);
}
T const & at (std::size_t idx) const
{
if ( idx >= Dim )
throw std::range_error("out of range");
return this->operator[](idx);
}
};
int main ()
{
Foo<float, 2U> foo;
foo.get<0U>();
foo.get<1U>();
//foo.get<2U>(); // compiler error
for ( auto i = 0U ; i < 5U ; ++i )
foo.at(i); // run time exception when i > 1
return 0;
}