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

时间:2012-01-28 13:33:45

标签: c++ gcc c++11 metaprogramming 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 =&gt; g++ (GCC) 4.6.2 20120120 (prerelease)

clang++ --version =&gt; clang version 3.0 (tags/RELEASE_30/final)(3.0.1)

4 个答案:

答案 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'有三个级别:

  1. template int参数,或(非VLA)数组大小// 必须为常量表达式的东西
  2. constexpr // 可能是一个常量表达式
  3. 非恒定表达
  4. 您无法将该列表中较低的项目转换为该列表中较高的项目,但显然可能是其他路径。

    例如,调用此函数

    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类型的参数。一个将其参数作为模板参数,另一个作为“正常”参数。 我将调用两个函数funcTfuncN而不是tfuncfunc。 您希望能够从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。

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