在编译时填充std ::数组,使用const_cast填充可能未定义的行为 [英] Filling a std::array at compile time and possible undefined behaviour with const_cast

查看:168
本文介绍了在编译时填充std ::数组,使用const_cast填充可能未定义的行为的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

已知 std :: array ::运算符[] ,因为C ++ 14是 constexpr ,请参见以下声明:

It is known that std::array::operator[] since C++14 is constexpr, see declaration below:

constexpr const_reference operator[]( size_type pos ) const; 

但是,它也是 const 合格。如果您想要使用 std :: array 的下标运算符,以便在编译时为您的数组赋值,就会产生影响。例如考虑以下用户文字:

However, it is also const qualified. This causes implications if you want to use the subscript operator of a std::array in order to assign values to your array at compile time. For example consider the following user literal:

template<typename T, int N>
struct FooLiteral {
  std::array<T, N> arr;
  constexpr FooLiteral() : arr {} { for(int i(0); i < N; ++i) arr[i] = T{42 + i}; }
};

如果试图声明一个 constexpr FooLiteral 类型的变量。这是因为重载解析规则将数组下标运算符的非const限定的非constexpr重载限定为更好的匹配。因此,编译器抱怨调用一个非 - constexpr 函数。

The above code won't compile if you try to declare a constexpr variable of type FooLiteral. This is attributed to the fact that overload resolution rules qualify the non-const qualified, non-constexpr overload of the array's subscript operator as a better match. Thus the compiler complains about calling a non-constexpr function.

现场演示

out是什么原因让委员会声明这个重载为 const 限定为C ++ 14,但是似乎意味着被注意到,还有一个建议 p0107R0 修复这在上升的C + +17。

I can't figure out what was the reason for the commitee to declare this overload as const qualified for C++14, however it seems that the implication is being noticed and there's also a proposal p0107R0 to fix this in the upcomming C++17.

我自然的,虽然为了克服这个C ++ 14是以某种方式黑客的表达式,以唤起正确的下标运算符。我做了以下:

My natural though to overcome this for C++14 was to somehow hack the expression, in order to evoke the correct subscript operator. What I did is the following:

template<typename T, int N>
struct FooLiteral {
  std::array<T, N> arr;
  constexpr FooLiteral() : arr {} { 
    for(int i(0); i < N; ++i) {
      const_cast<T&>(static_cast<const std::array<T, N>&>(arr)[i]) = T{42 + i};
    }
  }
};

Live Demo

这是我将数组转换为 const 引用以引发正确的下标运算符重载,然后我 const_cast 将重载的下标运算符的返回对象 T&

That is I casted the array to const reference to evoke the correct subscript operator overload and then I const_cast the returned object of the overloaded subscript operator to T& in order remove its const-ness and be able to assign to it.

这很好,但我知道 const_cast 应谨慎使用,坦率地说,我有第二个想法,如果这个黑客可以导致未定义的行为。

This works fine, but I know that const_cast should be used with caution and to be frank I have second thoughts about if this hack can cause undefined behaviour.

直观地,我不认为有问题,因为 const_cast 正在编译时初始化,我不能想到在这个状态可能出现的暗示。

Intuitively, I don't think there's a problem, since this const_cast is taking place at compile time initialization thus, I can't think of an implication that can arise at this state.

但是是这样,或者我错了,这会介绍UB到程序?

But is that so, or am I wrong and this introduce UB to the program?

有人可以证明这是UB吗?

Can someone justify if this is a UB or not?

推荐答案

据我所知,这不是未定义的行为。 将constexpr添加到 operator [] 发生在更改,从constexpr成员函数删除隐式常量。因此,它看起来像是他们只是添加在constexpr没有反映保持 const 的需要。

As far as I can tell this is not undefined behavior. The proposal that added constexpr to operator[] happened before the changes that removed the implicit const from constexpr member functions. So it looks like they just added on constexpr without reflecting on the need for keeping const or not.

我们可以看到形式早期版本放松对constexpr函数的约束下面是关于在常量表达式中对文字进行变异:

We can see form an earlier version of Relaxing constraints on constexpr functions that it says the following about mutating literals within a constant expression:


在常量表达式中创建的对象可以在常量表达式包括对任何constexpr函数调用的评估),直到该常量表达式的求值结束,或对象的生命周期结束,以较早发生者为准。它们不能被稍后的常量表达式求值修改。 [...]

Objects created within a constant expression can be modified within the evalution of that constant expression (including the evaluation of any constexpr function calls it makes), until the evaluation of that constant expression ends, or the lifetime of the object ends, whichever happens sooner. They cannot be modified by later constant expression evaluations. [...]

这种方法允许评估内的任意变量突变,同时仍然保留恒定表达式计算独立于程序的可变全局状态的必要属性。因此,不管何时进行评估,常数表达式评估为相同的值,除非当值未指定时(例如,浮点计算可以给出不同的结果,对于这些变化,不同的评估顺序也可以给出不同的结果) 。

This approach allows arbitrary variable mutations within an evaluation, while still preserving the essential property that constant expression evaluation is independent of the mutable global state of the program. Thus a constant expression evaluates to the same value no matter when it is evaluated, excepting when the value is unspecified (for instance, floating-point calculations can give different results and, with these changes, differing orders of evaluation can also give different results).

,我们可以看到早前提到的引用指向 const_cast hack它说:

and we can see the earlier proposal I referenced points out the const_cast hack and it says:


在C ++ 11中,constexpr成员函数是隐式的const。这对于希望在常量表达式内部和外部可以使用的文字类类型产生问题:

In C++11, constexpr member functions are implicitly const. This creates problems for literal class types which desire to be usable both within constant expressions and outside them:

[...]

已建议多种替代方案来解决此问题:

Several alternatives have been suggested to resolve this problem:


  • 接受现状,并要求用户工作

  • Accept the status quo, and require users to work around this minor embarrassment with const_cast.

这篇关于在编译时填充std ::数组,使用const_cast填充可能未定义的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆