2017-02-12 21 views
4

Есть ли способ статически утверждать индексы, известные во время компиляции и времени выполнения, в противном случае? Пример:static_assert по индексам, которые известны во время компиляции

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; 
} 
+0

спасибо, обновленный вопрос. – sharvey

+0

Вы можете посмотреть на __builtin_constant_p для GCC, но вряд ли вы получите идеальное решение, потому что есть некоторые действительно странные поведения с GCC, когда вы пытаетесь сделать то, что вы предлагаете. –

+0

Ваш 'foo [2]' доступ не выполняется в контексте 'constexpr', и ваш оператор также не является' constexpr'. Вы не получите ошибку времени компиляции. Чтобы достичь своей цели, используйте 'assert' (который будет выдавать ошибку' 'nonstockpr'', если false) вместо' static_assert'. –

ответ

1

Я не думаю, что возможно получить то, что вы хотите с помощью одной функции.

Даже если у вас есть функция constexpr, я не думаю, что вы сможете определить, когда выполняется время выполнения и когда выполняется время компиляции, и действовать по-другому.

Но вы можете разработать различные функции.

К примеру, шаблон get<>(), где аргумент шаблона является индексом, который может быть использован только с индексом известного во время компиляции и что может выполнить static_assert(), и at(std::size_t), который может принимать индекс, вычисленный по время выполнения с проверкой времени выполнения.

Мимоходом:

1) Я полагаю, как обычно в STL, использование at() для связанного проверяется доступ и operator[]() для связанного неконтролируемого доступа

2), и я предложить использование индекс без знака или вы должны проверить, что индекс равен >= 0.

Ниже приведен рабочий пример

#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; 
} 
+0

Спасибо за этот ответ, он работает отлично. Любопытно, можно ли написать функцию с поведением как 'get', так и' at', в зависимости от того, известен ли параметр index во время компиляции? – sharvey

+0

@sharvey - Я не думаю, что это возможно; если вы обнаружите, что это можно сделать, пожалуйста, покажите мне, как это сделать. – max66

3

Вы можете просто выбросить исключение или утверждать. Он не сможет выполнить компиляцию в контексте constexpr. Это работает только в том случае, если условие метания может быть оценено в контексте constexpr. Обратите внимание, что в некоторых версиях gcc есть ошибка, которая предотвращает работу броска.