С++ 11 аргумент функции constexpr, переданный в аргументе шаблона

Это работало несколько недель назад:

template <typename T, T t>
T            tfunc()
{
    return t + 10;
}

template <typename T>
constexpr T       func(T t)
{
    return tfunc<T, t>();
}

int main()
{
    std::cout << func(10) << std::endl;
    return 0;
}

Но теперь g++ -std=c++0x говорит:

main.cpp: In function ‘constexpr T func(T) [with T = int]’:
main.cpp:29:25:   instantiated from here
main.cpp:24:24: error: no matching function for call to ‘tfunc()’
main.cpp:24:24: note: candidate is:
main.cpp:16:14: note: template<class T, T t> T tfunc()
main.cpp:25:1: warning: control reaches end of non-void function [-Wreturn-type]

clang++ -std=c++11 говорит, что параметры шаблона tfunc<T, t>() игнорируются из-за недействительности.

Это ошибка или исправление?

PS:

g++ --version = > g++ (GCC) 4.6.2 20120120 (prerelease)

clang++ --version = > clang version 3.0 (tags/RELEASE_30/final) (3.0.1)

Ответы

Ответ 1

Параметр t не является постоянным выражением. Отсюда и ошибка. Следует также отметить, что оно не может быть постоянным выражением.

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

Так как t не является константным выражением, он не может использоваться в качестве аргумента шаблона:

return tfunc<T, t>(); //the second argument must be a constant expression

Возможно, вам нужно что-то вроде этого:

template <typename T, T t>
T  tfunc()
{
    return t + 10;
}

template <typename T, T t>  //<---- t became template argument!
constexpr T  func()
{
    return tfunc<T, t>();
}

#define FUNC(a)  func<decltype(a),a>()

int main()
{
    std::cout << FUNC(10) << std::endl;
}

Теперь он должен работать: онлайн-демонстрация

Ответ 2

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

На практике это означает, что компилятор не знает, как реализовать эту функцию во время выполнения:

template <typename T>
constexpr T       func(T t)
{
    return tfunc<T, t>();
}

Обходной путь заключается в том, чтобы изменить конструктор таким образом, что он принимает свой параметр t как обычный параметр, а не как параметр шаблона, и пометьте конструктор как constexpr:

template <typename T>
constexpr T       tfunc(T t)
{
    return t + 10;
}
template <typename T>
constexpr T       func(T t)
{
    return tfunc<T>(t);
}

Существует три уровня "констант-выражение-нежность":

  • параметр шаблона int или (не-VLA) размер массива//Что-то, что должно быть константным выражением
  • constexpr//Что-то, что может быть константным выражением
  • непостоянное выражение

Вы не можете преобразовать элементы, которые являются низкими в этом списке, в то, что является большим в этом списке, но, очевидно, на другом его пути.

Например, вызов этой функции

constexpr int foo(int x) { return x+1; }

не обязательно является константным выражением.

// g++-4.6 used in these few lines. ideone doesn't like this code. I don't know why
int array[foo(3)]; // this is OK
int c = getchar();
int array[foo(c)]; // this will not compile (without VLAs)

Таким образом, возвращаемое значение из функции constexpr является константным выражением, только если все параметры и реализация функции могут быть завершены во время компиляции.

Ответ 3

Повторите вопрос: у вас есть две функции, которые принимают параметр типа T. Один принимает свой параметр как параметр шаблона, а другой - как "нормальный" параметр. Я буду называть две функции funcT и funcN вместо tfunc и func. Вы хотите позвонить funcT из funcN. Отметить последнее как constexpr не помогает.

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

Невозможно реализовать funcN для запуска в режиме выполнения простым способом, поскольку он должен был бы работать для всех возможных значений t. Это потребовало бы компилятора для экземпляра множества экземпляров tfunc, по одному для каждого значения t. Но вы можете обойти это, если вы готовы жить с небольшим подмножеством T. Существует ограничение шаблона-рекурсии 1024 в g++, поэтому вы можете легко обрабатывать 1024 значения T с помощью этого кода:

#include<iostream>
#include<functional>
#include<array>
using namespace std;

template <typename T, T t>
constexpr T funcT() {
        return t + 10;
}

template<typename T, T u>
constexpr T worker (T t) {
        return t==0 ? funcT<T,u>() : worker<T, u+1>(t-1);

}
template<>
constexpr int worker<int,1000> (int ) {
            return -1;
}


template <typename T>
constexpr T       funcN(T t)
{
        return t<1000 ? worker<T,0>(t) : -1;
}

int main()
{
    std::cout << funcN(10) << std::endl;
    array<int, funcN(10)> a; // to verify that funcN(10) returns a constant-expression
    return 0;
}

Он использует функцию worker, которая рекурсивно преобразует "нормальный" параметр T в параметр шаблона u, который затем использует для создания и выполнения tfunc<T,u>.

Важнейшей линией является return funcT<T,u>() : worker<T, u+1>(t-1);

У этого есть ограничения. Если вы хотите использовать long или другие интегральные типы, вам придется добавить еще одну специализацию. Очевидно, что этот код работает только для t от 0 до 1000 - точный верхний предел, вероятно, зависит от компилятора. Другим вариантом может быть использование двоичного поиска сортов с другой рабочей функцией для каждой мощности 2:

template<typename T, T u>
constexpr T worker4096 (T t) {
        return t>=4096 ? worker2048<T, u+4096>(t-4096) : worker2048<T, u>(t);

}

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

Ответ 4

Похоже, что он должен дать ошибку - он не знает, что вы передали постоянное значение как t для func.

В общем случае вы не можете использовать значения времени выполнения в качестве аргументов шаблона. Шаблоны по своей сути являются конструкцией времени компиляции.