Определить литералы и константы времени компиляции - PullRequest
0 голосов
/ 03 июня 2018

Скажем, я хотел написать общий класс, который поддерживает целое число, которое всегда остается между двумя значениями.Примерно так:

template<int Lower, int Upper>
class MyInt {
    private:
        int m_int;
    public:
        // Constructors, operators...
};

Инвариант класса - это Lower <= m_int <= Upper.Конечно, в MyInt должны быть все обычные операции, которые обычно имеют целые числа, такие как операторы присваивания и арифметики.MyInt сгенерирует, если операция оставит его в состоянии, которое нарушает его инвариант.Однако во многих случаях это должно быть обнаружено во время компиляции.Рассмотрим пример этого кода:

int foo = 500;
constexpr int const bar = 500;
MyInt<0,100> a = 15; // OK
MyInt<0,100> b = foo; // Throws at runtime
MyInt<0,100> c = 500; // Compile error?
MyInt<0,100> d = bar; // Compile error?
MyInt<0,100> f = std::integral_constant<int, 500>; // Compile error

Для std::integral_constant написать соответствующий конструктор просто.Но возможно ли во время компиляции обнаружить, что a находится в пределах диапазона, а c и d - нет?Назначенные значения - это литералы, известные во время компиляции, или константы constexpr.

Я пробовал работать с SFINA и все такое, но я не смог найти способ перейти от семантики значений к аргументам шаблона, даже для этихслучаи, когда (я утверждаю) значения явно являются константами времени компиляции.

Верно ли для C ++ 17, что язык не предоставляет инструменты, необходимые для реализации этого?Если да, изменится ли это с наступающим C ++ 20?В случае, если то, что я ищу, невозможно, каковы причины этого?Мой интерес полностью образовательный, поэтому мне было бы интересно, если бы я что-то упустил (например, литералы, которые на самом деле не являются константами времени компиляции или что-то в этом роде).

Примечание: я знаю, что дело f может быть сделаноменее уродливо, вводя пользовательский буквальный суффикс и требуя от пользователя напечатать что-то вроде этого:

MyInt<0,100> g = 15_constint; // OK
MyInt<0,100> h = 500_constint; // Compile error

Мне также известна возможность предоставить такой интерфейс:

MyInt<0,100> i;
i.assign<500>(); // Compile error

Однако оба эти обходные пути сопряжены с определенными накладными расходами на ввод текста и не обязательно являются идиоматическими C ++.

Ответы [ 2 ]

0 голосов
/ 03 июня 2018

Попробуйте это.Это пример, который довольно просто масштабируется:

template<int Max, int Min>
class MyInt
{
public:
    constexpr MyInt(int i) : m_int(i > Max ? throw std::exception("out of bouonds int") : ( i < Min ? throw std::exception("out of bouonds int") : i)) {}
private:
    int m_int;
};


int main()
{
    MyInt<1, 2> i(4);
}

Это показывает ошибку времени компиляции :).Я использовал этот вопрос в качестве руководства: конструктор constexpr с проверкой времени компиляции

0 голосов
/ 03 июня 2018

Если то, что я ищу, невозможно, каковы причины этого?

В основном все сводится к тому, что функциональная модель C ++ не разрешает функциюраспознать различие между параметром constexpr и параметром времени выполнения.Если функция принимает int, то она принимает int.Поведение этой функции не может отличаться в зависимости от того, предоставляется ли int постоянным выражением (например, литералом) или значением времени выполнения.

И это в целом верно даже для constexpr функций .У вас не может быть функции constexpr, которая может проверять параметры во время компиляции.Оценка может быть выполнена во время компиляции, но выполнение foo(500); должно в конечном итоге вести себя так же, как и выполнение auto b = 500; foo(b);.

Вот почему все обходные пути включают использование трюков для помещения константного выражения в аргумент шаблонагде такое признание возможно.Конечно, аргумент шаблона не может никогда быть значением времени выполнения, что создает другие проблемы (например, наличие нескольких функций).А работать с шаблонным аргументом - обычно болезненная вещь.

По сути, для того, чтобы вы хотели, требуется, чтобы функция объявила, что параметр равен constexpr (наряду с правилами перегрузки, чтобы вы могли иметь не- constexpr версия).Ни одно такое предложение не было проголосовано в C ++ 20.Лучшее, что было сделано, это P1045 , который еще даже не обсуждался комитетом и имеет кучу дыр, которые нужно заполнить. Поэтому я не задерживаю дыхание.

...