虽然我之前使用过这样的代码,但很明显编译器有足够的信息可以工作,但我真的不明白为什么这会编译:
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;
}
实例:http://coliru.stacked-crooked.com/a/fc9cc6b954912bc5。
似乎可以同时使用gcc和clang。问题是虽然integral_constant
对存储的整数进行constexpr
转换,但constexpr
成员函数隐式地将对象本身作为参数,因此这样的函数不能用于{ {1}}上下文除非我们调用成员函数本身的对象可以被视为constexpr
。
此处,constexpr
是传递给i
的参数,因此foo
肯定不能被视为i
。然而,确实如此。一个更简单的例子:
constexpr
只要将template <class I>
void foo(I i) {
constexpr std::size_t j = i;
}
传递给std::integral_constant<std::size_t, 0>{}
,就会编译。
我觉得我错过了foo
规则的明显内容。无状态类型或其他类型是否有例外? (或者,也许是两个主要编译器中的编译器错误?这段代码似乎适用于clang 5和gcc 7.2)。
编辑:答案已经发布,但我认为这还不够。特别是,鉴于constexpr
的最后定义,为什么:
foo
编译,但不是:
foo(std::integral_constant<std::size_t, 0>{});
0和foo(0);
都是常量表达式。
编辑2:看起来它归结为即使对不是常量表达式的对象调用std::integral_constant<std::size_t, 0>{}
成员函数,也可以将其视为常量表达式,只要{{ 1}}未使用。这显然是显而易见的。我不认为这很明显:
constexpr
这不会编译,因为传递给this
的{{1}}不是常量表达式。它没用就没关系。因此,完整的答案需要显示标准中的某种语言,以证明constexpr int foo(int x, int y) { return x; }
constexpr void bar(int y) { constexpr auto x = foo(0, y); }
成员函数可以用作常量表达式,即使在非常量表达式对象上也是如此,只要{{ 1}}未使用。
答案 0 :(得分:18)
编译时常量表达式的规则随constexpr
改变,很多,但它们不是新的。在constexpr
之前,已经存在编译时常量表达式......并且旧规则在新规范中作为特殊情况保留,以避免破坏大量现有代码。
在大多数情况下,旧规则处理积分类型的编译时常量aka 积分常量表达式 ...这正是您正在处理的情况。所以不,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
对于std::integral_constant
成员函数,您可以在*this
函数中考虑i
和blorg
一样 - 如果执行没有取消引用它,它就可以了传递而不是编译时常量。
答案 1 :(得分:12)
这是有效的原因:
self.SubViewModel = {
treeViewOptions: {
items: Stores,
dataStructure: "plain",
showCheckBoxesMode: true,
searchValue: self.PackSearch,
parentIdExpr: "Key",
keyExpr: "ID",
displayExpr: "Name",
selectionMode: "single",
expandAllEnabled: true,
expandeExpr: "expanded",
expandNodesRecursive: true,
}
};
是因为它没有失败的原因适用。当template <class T, class I>
auto foo(const T& t, I i) {
return std::get<i>(t);
}
是i
时,std::integral_constant<size_t, S>
可以用作i
类型的转换常量表达式,因为该表达式遍历size_t
,它只返回一个模板参数(prvalue)作为值。这是一个完全有效的常量表达式。请注意,constexpr operator size_t()
未被引用 - 只是因为它的成员函数本身并不违反常量表达式约束。
基本上,没有什么&#34;运行时&#34;关于this
这里。
另一方面,如果i
是i
(通过int
),则调用foo(0)
将涉及{的{左值 - 右值转换{1}},但是这种情况不符合any of the criteria,因为std::get<i>
没有先前的初始化与常量表达式,它不是字符串文字,它没有用{{定义1}},它并没有在这个表达式中开始它的生命周期。