C ++ 11 аргумент функции constexpr передан в аргументе шаблона - PullRequest
15 голосов
/ 28 января 2012

Раньше это работало несколько недель назад:

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)

Ответы [ 4 ]

9 голосов
/ 28 января 2012

Параметр 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 голосов
/ 29 января 2012

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

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

Невозможно реализовать funcN для запуска во время выполнения простым способом, так как он должен был бы работать для all возможные значения t .Это потребует от компилятора создания множества экземпляров tfunc, по одному для каждого значения t.Но вы можете обойти это, если хотите жить с небольшим подмножеством T. В g ++ существует предел рекурсии шаблона 1024, поэтому вы можете легко обработать 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);

}

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

2 голосов
/ 28 января 2012

Мне кажется, что constexpr также должно быть допустимым в контексте времени выполнения, а не только во время компиляции.Обозначение функции как 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);
}

Существует три уровня 'constant-expression-ness':

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

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

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

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 является константным выражением , только если все параметры и реализация функции, может быть завершено во время компиляции.

1 голос
/ 28 января 2012

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

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

...