C++11 constexpr 函数的参数在模板参数中传递

发布于 2024-12-29 15:19:02 字数 1028 浏览 3 评论 0原文

这在几周前是有效的:

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() 由于无效而被忽略。

这是一个错误,还是一个修复?

PS:

g++ --version => g++ (GCC) 4.6.2 20120120(预发布)

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

This used to work some weeks ago:

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

But now g++ -std=c++0x says:

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 says that template's parameters of tfunc<T, t>() are ignored because invalid.

Is that a bug, or a fix ?

PS:

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

暖风昔人 2025-01-05 15:19:02

参数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;
}

现在它应该可以工作:在线演示

The parameter t is not a constant expression. Hence the error. It should be also noted that it cannot be a constant expression.

You can pass the constant expression as argument, but inside the function, the object (the parameter) which holds the value, is not a constant expression.

Since t is not a constant expression, it cannot be used as template argument:

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

Maybe, you want something like this:

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

Now it should work : online demo

泪眸﹌ 2025-01-05 15:19:02

我感觉 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);
}

“常量表达式性质”分为三个级别:

  1. 模板 int 参数,或(非 VLA)数组大小 // 必须< /em> 是一个常量表达式
  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 函数的返回值才是常量表达式。

I get the feeling that constexpr must also be valid in a 'runtime' context, not just at compile-time. Marking a function as constexpr encourages the compiler to try to evaluate it at compile-time, but the function must still have a valid run-time implementation.

In practice, this means that the compiler doesn't know how to implement this function at runtime:

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

A workaround is to change the constructor such that it takes its t parameter as a normal parameter, not as a template parameter, and mark the constructor as constexpr:

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

There are three levels of 'constant-expression-ness':

  1. template int parameter, or (non-VLA) array size // Something that must be a constant-expression
  2. constexpr // Something that may be a constant-expression
  3. non-constant-expression

You can't really convert items that are low in that list into something that is high in that list, but obviously the other route it possible.

For example, a call to this function

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

isn't necessarily a constant-expression.

// 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)

So the return value from a constexpr function is a constant expression only if all the parameters, and the implementation of the function, can be completed at executed at compile-time.

盛夏尉蓝 2025-01-05 15:19:02

回顾一下这个问题:您有两个函数,它们采用 T 类型的参数。一个将其参数作为模板参数,另一个将其作为“普通”参数。
我将调用两个函数 funcTfuncN,而不是 tfuncfunc
您希望能够从 funcN 调用 funcT。将后者标记为 constexpr 没有帮助。

任何标记为 constexpr 的函数都必须是可编译的,就好像 constexpr 不存在一样。 constexpr 函数有点精神分裂。它们仅在某些情况下才会升级为完整的常量表达式。

不可能以简单的方式实现 funcN 在运行时运行,因为它需要能够适用于 t所有可能值。这将要求编译器实例化许多 tfunc 实例,每个 t 值一个实例。但是,如果您愿意使用 T 的一小部分子集,则可以解决此问题。g++ 中的模板递归限制为 1024,因此您可以使用以下代码轻松处理 T 的 1024 个值:

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

它使用函数 < code>worker 它将递归地将“正常”参数 t 转换为模板参数 u,然后用它来实例化和执行tfunc

关键行是 return funcT() :worker(t-1);

这有局限性。如果您想使用long或其他整数类型,则必须添加另一个专门化。显然,此代码仅适用于 0 到 1000 之间的 t - 确切的上限可能取决于编译器。另一种选择可能是使用某种二分搜索,对 2 的每个幂使用不同的工作函数:

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

}

我认为这可以解决模板递归限制,但它仍然需要大量的实例化,并且会使如果它能工作的话,编译非常慢。

Recap the question: You have two functions which take a parameter of type T. One takes its parameter as a template parameter, and the other as a 'normal' parameter.
I'm going to call the two functions funcT and funcN instead of tfunc and func.
You wish to be able to call funcT from funcN. Marking the latter as a constexpr doesn't help.

Any function marked as constexpr must be compilable as if the constexpr wasn't there. constexpr functions are a little schizophrenic. They only graduate to full constant-expressions in certain circumstances.

It would not be possible to implement funcN to run at runtime in a simple way, as it would need to be able to work for all possible values of t. This would require the compiler to instantiate many instances of tfunc, one for each value of t. But you can work around this if you're willing to live with a small subset of T. There is a template-recursion limit of 1024 in g++, so you can easily handle 1024 values of T with this code:

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

It uses a function worker which will recursively convert the 'normal' parameter t into a template parameter u, which it then uses to instantiate and execute tfunc<T,u>.

The crucial line is return funcT<T,u>() : worker<T, u+1>(t-1);

This has limitations. If you want to use long, or other integral types, you'll have to add another specialization. Obviously, this code only works for t between 0 and 1000 - the exact upper limit is probably compiler-dependent. Another option might be to use a binary search of sorts, with a different worker function for each power of 2:

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

}

I think this will work around the template-recursion-limit, but it will still require a very large number of instantiations and would make compilation very slow, if it works at all.

遗失的美好 2025-01-05 15:19:02

看起来它应该给出一个错误 - 它无法知道您将一个常量值作为 t 传递给了 func。

更一般地说,您不能使用运行时值作为模板参数。模板本质上是一个编译时构造。

Looks like it should give an error - it has no way of knowing that you passed in a constant value as t to func.

More generally, you can't use runtime values as template arguments. Templates are inherently a compile-time construct.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文