Nir*_*man 17 c++ templates constexpr c++14
虽然我之前使用过这样的代码,但很明显编译器有足够的信息可以工作,但我真的不明白为什么这会编译:
template <class T, class I>
auto foo(const T& t, I i) {
return std::get<i>(t);
}
int main()
{
std::cerr << foo(std::make_tuple(3,4), std::integral_constant<std::size_t, 0>{});
return 0;
}
Run Code Online (Sandbox Code Playgroud)
实例:http://coliru.stacked-crooked.com/a/fc9cc6b954912bc5.
似乎可以同时使用gcc和clang.的事情是,同时integral_constant具有constexpr转换所存储的整数,constexpr成员函数隐式地把对象本身作为参数,并且因此不能被用在这样的函数constexpr的上下文,除非我们正在呼叫的成员函数的对象本身可以作为被处理constexpr.
这里i是一个传递给的参数foo,因此i绝对不能被视为constexpr.然而,确实如此.一个更简单的例子:
template <class I>
void foo(I i) {
constexpr std::size_t j = i;
}
Run Code Online (Sandbox Code Playgroud)
这也是编译,只要std::integral_constant<std::size_t, 0>{}传递给foo.
我觉得我错过了一些关于constexpr规则的明显内容.无状态类型或其他类型是否有例外?(或者,也许是两个主要编译器中的编译器错误?这个代码似乎适用于clang 5和gcc 7.2).
编辑:答案已经发布,但我认为这还不够.特别是,鉴于最后的定义foo,为什么:
foo(std::integral_constant<std::size_t, 0>{});
Run Code Online (Sandbox Code Playgroud)
编译,但不是:
foo(0);
Run Code Online (Sandbox Code Playgroud)
0和std::integral_constant<std::size_t, 0>{}都是常量表达式.
编辑2:似乎它归结为constexpr即使对不是常量表达式的对象调用成员函数这一事实本身也可以被视为常量表达式,只要this未使用即可.这显然是显而易见的.我不认为这很明显:
constexpr int foo(int x, int y) { return x; }
constexpr void bar(int y) { constexpr auto x = foo(0, y); }
Run Code Online (Sandbox Code Playgroud)
这不会编译,因为y传入foo的不是常量表达式.它没用就没关系.因此,完整的答案需要从标准中显示某种语言,以证明constexpr成员函数可以用作常量表达式,即使在非常量表达式对象上,只要this未使用.
Ben*_*igt 18
编译时常量表达式的规则随着constexprA LOT 改变,但它们并不是新的.以前constexpr,已经有编译时常量表达式...旧规则在新规范中作为特殊情况保留,以避免破坏大量现有代码.
在大多数情况下,旧规则处理整数类型的编译时常量,即整数常量表达式 ...这正是您正在处理的情况.所以不,constexpr规则中没有任何奇怪的东西......这是其他与之无关的旧规则constexpr.
条件表达式e是核心常量表达式,除非根据抽象机器的规则评估e将评估以下表达式之一:
...
- 除非适用,否则左值转换为左值
- 一个非整数或枚举类型的非易失性glvalue,它引用一个完整的非易失性const对象,具有前面的初始化,用常量表达式初始化,或者
- 一个非易失性glvalue,引用字符串文字的子对象,或
- 一个非易失性glvalue,指的是一个非易失性对象
constexpr,或者指的是这样一个对象的非可变子对象,或者- 文字类型的非易失性glvalue,指的是一个非易失性对象,其生命周期始于e的评估范围内;
你是对的,第三个子弹不适用.但第一个确实如此.
因此,您可以在新规则之间进行有趣的相互作用,这些规则允许函数返回为编译时常量,具体取决于抽象机器上的评估规则,以及允许积分值为编译时常量的常规行为,即使未标记为此类.
这里有一个简单的例子,为什么this隐含的参数无关紧要.作为参数并不意味着评估对象:
constexpr int blorg(bool const flag, int const& input)
{
return flag? 42: input;
}
int i = 5; // 5 is an integral constant expression, but `i` is not
constexpr int x = blorg(true, i); // ok, `i` was an argument but never evaluated
constexpr int y = blorg(false, i); // no way
Run Code Online (Sandbox Code Playgroud)
对于std::integral_constant成员函数,您可以*this像i在blorg函数中一样考虑- 如果执行没有取消引用它,则可以在不作为编译时常量的情况下传递它.
Bar*_*rry 12
这有效的原因:
template <class T, class I>
auto foo(const T& t, I i) {
return std::get<i>(t);
}
Run Code Online (Sandbox Code Playgroud)
是因为它没有失败的原因适用.when i是a时std::integral_constant<size_t, S>,i可以用作类型的转换常量表达式,size_t因为该表达式通过constexpr operator size_t(),它只返回一个模板参数(它是一个prvalue)作为值.这是一个完全有效的常量表达式.请注意,this未引用 - 只是因为它本身的成员函数本身并不违反常量表达式约束.
基本上,这里没有任何"超时" i.
另一方面,如果i是int(通过foo(0)),则调用std::get<i>将涉及左值到右值的转换i,但是这种情况不符合任何条件,因为i没有先前的初始化与常量表达式,它不是字符串文字,它没有定义constexpr,并且它没有在此表达式中开始它的生命周期.