16

这曾经在几周前起作用:

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

4 回答 4

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

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

于 2012-01-28T13:35:55.400 回答
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);
}

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

  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才是常量表达式。

于 2012-01-28T14:05:59.533 回答
2

回顾一下这个问题:你有两个函数接受 type 的参数T。一个将其参数作为模板参数,另一个作为“普通”参数。我将调用这两个函数funcTfuncN不是tfuncand func。您希望能够从funcT呼叫funcN。将后者标记为 aconstexpr没有帮助。

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

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

#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, 或其他整数类型,则必须添加另一个专业化。显然,此代码仅适用于 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);

}

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

于 2012-01-28T20:58:19.157 回答
1

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

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

于 2012-01-28T13:37:27.983 回答