Параметры функции constexpr как аргументы шаблона

Я играю с некоторым кодом игрушек, используя С++ 11, чтобы узнать немного больше о том, как все работает. Во время этого я наткнулся на следующий вопрос, который упрощает до:

template <int x, int y>
class add {
public:
    static constexpr int ret = x + y;
};

constexpr int addFunc(const int x, const int y) {
    return add<x,y>::ret;
}

int main() {
    const int x = 1;
    const int y = 2;
    cout << add<x,y>::ret << endl; // Works
    cout << addFunc(1,2) << endl;  // Compiler error
    return 0;
}

Я использую GCC 4.8.1, а вывод:
'x' не является постоянным выражением в аргументе шаблона для типа 'int'
'y' не является постоянным выражением в аргументе шаблона для типа 'int'

В чем именно разница между двумя способами, которые я пытаюсь рассчитать add::ret? Оба эти значения должны быть доступны во время компиляции.

Ответ 1

Расскажите компилятору, что addFunc будет constexpr. Но он зависит от параметров, которые не являются самими constexpr, поэтому компилятор уже задыхается от этого. Маркировка их const означает, что вы не собираетесь изменять их в теле функции, и конкретные вызовы, которые вы делаете с этой функцией, не рассматриваются на данном этапе.

Как вы можете заставить компилятор понять, что вы передадите константы времени компиляции в addFunc: сделайте параметры самими параметрами шаблона:

template <int x, int y>
constexpr int addFunc() {
    return add<x,y>::ret;
}

Затем вызовите

cout << addFunc<1,2>() << endl;

Ответ 2

Если ваша цель - просто немного сократить код, в С++ 14 вы можете создать шаблон переменной:

template <int x, int y>
constexpr int addVar = x + y;

cout << addVar<5, 6> << endl; // Works with clang 3.5, fails on GCC 4.9.1

GCC 5 также будет поддерживать это.

Ответ 3

Компилятор не знает, всегда ли доступны x и y во время компиляции как постоянные значения (выражение), а что еще, С++ 11/14 не поддерживает параметр функции constexpr, поэтому нет способа, чтобы x и y могли использоваться как параметр для шаблона add < > в addFunc.

Ответ 4

Функциональные параметры функции constexpr не являются постоянными выражениями. Функция constexpr внешняя (поскольку вызов может привести к постоянному выражению), но вычисления внутри являются такими же, как constexpr, поскольку они будут в нормальной функции.

Шаблон-аргументы требуют постоянных выражений. Это важнейшие требования к постоянным выражениям, которые не выполняются в вашем коде и, таким образом, приводят к ошибке компилятора ([expr.const]/2, focus mine):

Условное выражение является выражением постоянной константы, если оно включает одно из следующего в качестве потенциально оцениваемого подвыражения (3.2) [...]:

- преобразование lvalue-to-rvalue (4.1), если оно не применяется к

  • значение целочисленного или перечисляемого типа, которое относится к энергонезависимому объекту const с предшествующей инициализацией, инициализируется с постоянным выражением или
  • glvalue типа literal, который ссылается на энергонезависимый объект , определенный с помощью constexpr, или который ссылается на под-объект такого объект или
  • glvalue типа literal, который ссылается на энергонезависимый временный объект, срок жизни которого не закончился, инициализируется константой Выражение;

Вы применяете преобразование lvalue-to-rval для параметров, чтобы передать их в качестве аргументов шаблона.
Первый элемент маркера не применяется, поскольку параметр функции не инициализируется или не инициализируется с константным выражением, а второй и третий не являются (в частности, параметры функции не должны быть объявлены constexpr).