众所周知,std::array::operator[]
自C++ 14以来constexpr
,请参阅下面的声明:
constexpr const_reference operator[]( size_type pos ) const;
但是,它也是const
合格的.如果要使用a的下标运算符,std::array
以便在编译时为数组赋值,则会产生影响.例如,考虑以下用户文字:
templatestruct FooLiteral { std::array arr; constexpr FooLiteral() : arr {} { for(int i(0); i < N; ++i) arr[i] = T{42 + i}; } };
如果您尝试声明constexpr
类型的变量,则上述代码将无法编译FooLiteral
.这归因于重载决策规则将数组下标运算符的非const限定非constexpr重载限定为更好的匹配.因此编译器抱怨调用非constexpr
函数.
现场演示
我无法弄清楚什么是为commitee宣布此重载为理由const
胜任C++ 14,但似乎暗示被发现并还有一个建议p0107R0在upcomming C++ 17解决这个问题.
我自然而然地为C++ 14克服这个问题是为了唤起正确的下标运算符,以某种方式破解表达式.我做的是以下内容:
templatestruct FooLiteral { std::array arr; constexpr FooLiteral() : arr {} { for(int i(0); i < N; ++i) { const_cast (static_cast &>(arr)[i]) = T{42 + i}; } } };
现场演示
这就是我将数组转换为const
引用以引发正确的下标运算符重载,然后我const_cast
重载的下标运算符的返回对象,T&
以便删除它的常量并能够分配给它.
这工作正常,但我知道const_cast
应该谨慎使用,坦白说我还有第二个想法,如果这个黑客可以导致不明确的行为.
直觉上,我认为没有问题,因为这const_cast
是在编译时初始化时发生的,因此,我无法想到在这种状态下可能出现的含义.
但是这样,或者我错了,这将UB引入该计划?
如果这是一个UB,有人可以证明这一点吗?
据我所知,这不是未定义的行为.该提案所添加constexpr到operator[]
的发生之前,从constexpr成员函数除去隐常量的变化.所以看起来他们只是添加了constexpr而没有反映是否需要保持const.
我们可以在constexpr函数中看到一个早期版本的Relax约束,它说明了在常量表达式中改变文字的内容:
在常量表达式中创建的对象可以在对该常量表达式的求值中进行修改(包括对其所做的任何constexpr函数的求值),直到该常量表达式的求值结束,或者对象的生命周期结束,以较早者为准.它们不能通过以后的常量表达式评估来修改.[...]
这种方法允许评估中的任意变量突变,同时仍保留恒定表达评估独立于程序的可变全局状态的基本属性.因此,无论何时计算,常量表达式都会评估为相同的值,除非未指定值(例如,浮点计算可以给出不同的结果,并且通过这些更改,不同的评估顺序也可以给出不同的结果) .
我们可以看到我引用的早期提案指出了const_cast
黑客攻击并且它说:
在C++ 11中,constexpr成员函数是隐式const.这会为希望在常量表达式和外部表达式中使用的文字类类型带来问题:
[...]
已经提出了几种替代方案来解决这个问题:
接受现状,并要求用户使用const_cast解决这个轻微的尴尬.