C++ 可以在C+;中使用for循环实例化模板+;14 constexpr函数?

C++ 可以在C+;中使用for循环实例化模板+;14 constexpr函数?,c++,c++14,constexpr,C++,C++14,Constexpr,我一直在用一个SVN构建的clang来尝试constexpr的宽松规则。到目前为止,我还无法确定的一件事是,是否可以在编译时在constexpr函数中循环遍历元组中的元素 因为我没有C++14兼容的标准库可供测试,所以我准备了以下等效测试: template<int N> constexpr int foo() { return N; } constexpr int getSum() { auto sum = 0; for (auto i = 0; i < 10;

我一直在用一个SVN构建的clang来尝试
constexpr
的宽松规则。到目前为止,我还无法确定的一件事是,是否可以在编译时在constexpr函数中循环遍历元组中的元素

因为我没有C++14兼容的标准库可供测试,所以我准备了以下等效测试:

template<int N>
constexpr int foo() {
  return N;
}

constexpr int getSum() {
  auto sum = 0;
  for (auto i = 0; i < 10; ++i) {
    sum += foo<i>();
  }
  return sum;
}

constexpr auto sum = getSum();
在SVN clang中,我的第一个示例没有:

test2.cpp:19:12: error: no matching function for call to 'foo' sum += foo(); ^~~~~~ test2.cpp:11:15: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'N' constexpr int foo() { ^ test2.cpp:19:12:错误:调用“foo”时没有匹配的函数 sum+=foo(); ^~~~~~ test2.cpp:11:15:注意:忽略候选模板:显式指定无效 模板参数“N”的参数 constexpr int foo(){ ^ 首先,我很难解释这个错误消息的第二部分。除此之外,它是由C++14标准强制执行的吗?如果是的话,有人知道为什么不允许使用这种语法(简单的疏忽或防止某些事情发生)

除此之外,它是由C++14标准强制执行的吗?如果是的话,有人知道为什么不允许使用这种语法(简单的监督或保护不受影响)

这是因为
constexpr
并不是编译时计算或使用的专用函数。
constexpr
函数就是这样,允许函数(或变量)在常量表达式中使用。除此之外,它们是正则函数。在某些上下文中,例如
static\u assert
或数组大小等仅在编译时使用的情况下,恰好需要常量表达式

你会注意到在你的代码中你循环了一个变量,但是你循环的变量本身并不是
constexpr
,所以它不是一个常量表达式,不能在
N
的模板实例化中使用

constexpr bool f(int x) {
    static_assert(x > 10, "..."); // invalid
    return true;
}
这显然是无效的,因为正如我前面提到的,您不必在独占编译时情况下使用
constexpr
函数

constexpr int times_ten(int x) {
    return x * 10;
}

int main() {
   int a = times_ten(20); // notice, not constexpr
   static_assert(times_ten(20) == 200, "...");
   static_assert(a == 200, "..."); // doesn't compile
}

这对你来说可能太晚了,但对其他人来说可能会有用,我的答案如下

@Rapptz的答案没有错,但是你的问题的答案可能是“是”。是的,for循环不能像我们讨论的那样使用。但是你想在循环中迭代,不必使用for循环思维,例如使用递归,这是可能的。这更难看,但对于一些用户来说,从中得到一些沉重的东西运行时(不应在运行时中)编译时间可能是值得的。对于某些人来说,增加的丑陋可能不值得牺牲代码的清洁度,所以这取决于每个人的决定,我只是想证明这是可能的。资源受到严格限制的嵌入式领域可能值得考虑。通过这种递归,您可以描述许多算法,您可以ld可能会像prolog那样做tail+head,一次处理一个条目,或者复制数组,一次更改其中的一个条目(可怜人的以模板为中心的连接,而不更改返回类型的大小)。只是有一个限制,在编译时不会看到循环结束时的“if”条件。因此,典型的算法如下:

template<>
constexpr int getSum<maxLoopIndex-1>() {
  return foo<maxLoopIndex-1>();
}
模板
int factorialTemplateSimpler(){
如果(输入<2){
返回1;
}否则{
返回factorialTemplateSimpler()*输入;
}
}
不会编译,因为工具链会一直递减直到终止(可能在1000次递归之后)。要让模板看到结束状态,必须明确如下所示:

template<>
constexpr int getSum<maxLoopIndex-1>() {
  return foo<maxLoopIndex-1>();
}

模板
constexpr int foo(){
返回N;
}
const int maxLoopIndex=10;
模板
constexpr int getSum(){
返回foo()+getSum();
}
模板
constexpr int getSum(){
返回0;
}
int main(){
constexpr auto sum=getSum();
回报金额;
}
这就是你想要的,它是用C++14编译的,不知道为什么它也用C++11编译

许多算法都有一个特例来完成与典型算法不同的特殊任务,您必须对其进行单独的实现(因为在实例化时不会看到if)。并且您还必须在某个地方结束循环,因此您必须实现单独的退出案例。为了节省额外的键入,最好将退出案例和特殊案例放在同一索引中,这样您就不必创建重复的实现并增加维护。因此,您必须决定是否最好将其计入inc重排或递减方式。因此,您只需实现组合的特殊情况和退出情况一次。要将其放入上下文中,对于阶乘之类的东西,我将递减,这样0-情况和循环结束将使用相同的代码实现,然后让算法在从深度递归返回时完成工作

如果你没有任何特殊情况,你必须做一个特殊情况,例如在上面的代码中,我知道返回0是安全的,我知道当你数到10,但不包括它,因此我在索引10做了特殊情况,并返回了0

模板
constexpr int getSum(){
返回0;
}
如果你不可能做到这一点,那么你必须实现算法的一个子集(没有递归),但在索引9处停止,如下所示:

template<>
constexpr int getSum<maxLoopIndex-1>() {
  return foo<maxLoopIndex-1>();
}
模板
constexpr int getSum(){
返回foo();
}
注意:只要常量变量和等式在编译时,就可以使用它们

完整示例:

模板
constexpr int foo(){
返回N;
}
const int maxLoopIndex=10;
模板
constexpr int getSum(){
返回foo()+getSum();
}
模板
constexpr int getSum(){
返回foo();
}
int main(){
constexpr auto sum=getSum()
template<int N>
constexpr int foo() {
  return N;
}

template<int index>
constexpr int getSum() {
  return foo<index>() + getSum<index-1>();
}

template<>
constexpr int getSum<0>() {
  return foo<0>();
}

int main() {
    constexpr auto sum = getSum<10 - 1>();  // loop 0..9
    return sum;
}