过去几周曾经工作过:
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
=&gt; g++ (GCC) 4.6.2 20120120 (prerelease)
clang++ --version
=&gt; clang version 3.0 (tags/RELEASE_30/final)
(3.0.1)
答案 0 :(得分:9)
参数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;
}
现在应该有效:online demo
答案 1 :(得分:2)
我感觉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'有三个级别:
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
函数的返回值是一个常量表达式,只有所有参数,并且函数的实现可以在编译时执行。< / p>
答案 2 :(得分:2)
回顾一下问题:你有两个函数采用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
,它将递归地将'normal'参数t
转换为模板参数u
,然后用它来实例化并执行tfunc<T,u>
。
关键的一行是return funcT<T,u>() : worker<T, u+1>(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);
}
我认为这将解决模板递归限制,但它仍然需要大量的实例化,并且如果它完全有效,会使编译非常变慢。
答案 3 :(得分:1)
看起来应该会出错 - 它无法知道你将常量值作为t传递给func。
更一般地说,您不能将运行时值用作模板参数。模板本质上是一个编译时构造。