2017-08-01 107 views
17

考虑下面的代码:为什么不能在常量表达式中显示折叠表达式?

template<int value> 
constexpr int foo = value; 

template<typename... Ts> 
constexpr int sum(Ts... args) { 
    return foo<(args + ...)>; 
} 

int main() { 
    static_assert(sum(10, 1) == 11); 
} 

铛4.0.1使我有以下错误:

main.cpp:6:17: error: non-type template argument is not a constant expression 
    return foo<(args + ...)>; 
       ^~~~ 

这让我很吃惊。每个参数在编译时已知,sum被标记为constexpr,所以我没有看到为什么在编译时无法评估fold表达式。

当然,这也失败,出现相同的错误消息:

constexpr int result = (args + ...); // in sum 

[expr.prim.fold]是不是非常有帮助,这是非常短的,只允许描述的语法。

尝试更新版本的clang也给出了和gcc一样的结果。

他们真的被允许吗?

+0

折叠表达式在这里是一个冒烟的枪。 – bolov

+3

@bolov你的意思是红鲱鱼? – Oktalist

+0

@Oktalist是的。谢谢,这正是我所寻找的成语。我刚刚去了下一个最好的事情。 – bolov

回答

12

允许常量表达式包含折叠表达式。它是而不是允许使用函数参数的值,除非函数调用本身是整个常量表达式的一部分。举例来说:

constexpr int foo(int x) { 
    // bar<x>(); // ill-formed 
    return x; // ok 
} 
constexpr int y = foo(42); 

变量y需要用常数表达式进行初始化。 foo(42)是一个可接受的常量表达式,因为即使调用foo(42)涉及对参数x执行左值到右值转换以返回其值,该参数在范围内创建的整个常量表达式foo(42)因此它的值是静态已知的。但x本身并不是foo内的常量表达式。然而,在它出现的上下文中不是一个常量表达式的表达式可能是更大常量表达式的一部分。

非类型模板参数的参数本身必须是一个常量表达式,但x不是。所以被注释的行是不合格的。

同样,由于您对sum的参数执行左值到右值转换,因此您的(args + ...)未能成为常量表达式(因此不能用作模板参数)。但是,如果使用常量表达式参数调用函数sum,则作为整体的函数调用可以是常量表达式,即使其中出现(args + ...)也是如此。

5

这个问题的一些读者可能有兴趣知道如何修改OP:例子来编译和运行,因此我将这个附录包含在Brian:s excellent accepted answer中。

作为布赖恩描述中,可变参数函数的参数的值不在sum一个常量表达式(但不会导致foo不是常量表达式,只要该参数不“逃离”的foo范围;因为它已在常量表达式foo(42)内创建)。

运用这些知识来OP:为榜样,而不是使用逃逸sumconstexpr最近的范围内时,将不会被视为一个constexpr一个可变参数函数的参数,我们可以迁移的可变参数函数的参数是一个可变参数无类型的模板参数。

template<auto value> 
constexpr auto foo = value; 

template<auto... args> 
constexpr auto sum() { 
    return foo<(args + ...)>; 
} 

int main() { 
    static_assert(sum<10, 1, 3>() == 14); 
} 
3

您的问题与...无关。

template<class T0, class T1> 
constexpr int sum(T0 t0, T1 t1) { 
    return foo<(t0+t1)>; 
} 

这也以相同的方式失败。

从本质上讲,你的问题是constexpr函数必须可以被调用,而不是constexpr的参数。

这是对constexpr含义的常见误解:它并不意味着“总是constexpr”。

有复杂的标准子句说明这里出了什么问题,但其实质是在constexpr函数中,函数参数本身不被认为是constexpr。如果输入是,则函数的结果可以是,但在函数内,即使参数不是constexpr,代码也必须有效。

您仍然可以解决此问题:用户定义一个文字""_k,该文字解析整数并生成integral_constant

static_assert(sum(10_k, 1_k) == 11); 

将编译和运行,因为+对整型常量不依赖于这些变量是constexpr。或者,您可以将这些值作为非类型模板参数。 static_assert(sum < 10,1>()== 11);