从 float 转换为 int
7.3.11 浮点-整数转换
(1) 浮点类型的 prvalue 可以转换为整数类型的 prvalue。转换截断;也就是说,小数部分被舍去。如果截断后的值不能在目标类型中表示,则行为未定义。
因此,我们最终得到了一个漂亮的从 f
转换来的 int
值。1.2 const_cast
现在我们知道了static_cast<>
部分的返回值,我们可以专注于const_cast<int&>()
:
结果类型需要为:
7.6.1.11 const_cast(我强调)
(1) 表达式 const_cast<T>(v)
的结果类型为 T
。如果 T
是 对象类型的左值引用,则 结果是左值;如果 T
是对象类型的右值引用,则结果是 xvalue;否则,结果是 prvalue,并对表达式 v
执行 lvalue-to-rvalue、array-to-pointer 和 function-to-pointer 标准转换。可以使用 const_cast 显式地执行的转换如下所示。不得使用 const_cast 显式地执行任何其他转换。
static_cast<>
的结果是左值,因此 const_cast<>
的结果也必须是左值。
const_cast<>
执行什么转换?
7.6.1.11 const_cast(我强调)
(4) 对于两个对象类型 T1
和 T2
,如果可以使用 const_cast 将指向 T1
的指针显式转换为类型“指向 T2
的指针”,则还可以进行以下转换:
(4.1) 使用强制类型转换 const_cast<T2&>
,将类型为 T1
的左值显式转换为类型为 T2
的左值;
(4.2) 使用强制类型转换 const_cast<T2&&>
,将类型为 T1
的 glvalue 显式转换为类型为 T2
的 xvalue;以及
(4.3) 如果 T1
是类类型,则可以使用强制类型转换 const_cast<T2&&>
将类型为 T1
的 prvalue 显式转换为类型为 T2
的 xvalue。
如果操作数是 glvalue,则 reference const_cast 的结果引用原始对象;否则引用应用临时材料化的结果。
const_cast<>
将 lvalue const int&
转换为 int&
左值,它将引用同一对象。
1.3 结论
const_cast<int&>(static_cast<int const&>(f))
是合法的,并将得到一个左值 int 引用。
你甚至可以按照 6.7.7 临时对象 的规定延长引用的生命周期。
(6) 如果引用所绑定的glvalue是通过以下方式之一获得的,则引用所绑定的临时对象或作为其子对象完整对象的临时对象将在引用的生命周期内保持存在:
[...]
- (6.6) a
- (6.6.1) const_cast
(expr.const.cast),
[...]
将一个glvalue操作数转换为一个引用到指定操作数所指定的对象、其完整对象或其子对象的glvalue,而不需要用户定义的转换,
[...]
所以这也是合法的:
float const& f = 1.2f;
int& i = const_cast<int&>(static_cast<int const&>(f));
i++;
return i;
1.4笔记
2. 为什么 (int&)f
不起作用
从技术上讲,它应该起作用,因为C风格的转换允许执行此转换序列:
7.6.3 显式类型转换(强制类型转换)
(4) 通过以下方式进行的转换:
(4.1) const_cast
(expr.const.cast),
(4.2) static_cast
(expr.static.cast),
(4.3) static_cast
后跟 const_cast
,
(4.4) reinterpret_cast
(expr.reinterpret.cast),或
(4.5) reinterpret_cast
后跟 const_cast
,
可以使用显式类型转换的强制类型转换符号进行。同样的语义限制和行为适用于这些转换,[...]。
所以const_cast<int&>(static_cast<int const&>(f))
应该是一个有效的转换序列。
这不起作用的原因实际上是一个非常古老的编译器错误。
According to 7.6.3 [expr.cast] paragraph 4, one possible interpretation of an old-style cast is as a static_cast followed by a const_cast. One would therefore expect that the expressions marked #1 and #2 in the following example would have the same validity and meaning:
struct S {
operator const int* ();
};
void f(S& s) {
const_cast<int*>(static_cast<const int*>(s));
(int*) s;
}
However, a number of implementations issue an error on #2.
Is the intent that (T*)x
should be interpreted as something like const_cast<T*>(static_cast<const volatile T*>(x))
结果如下:
理由(2009年7月):
根据文字的直接解释,该示例应该有效。这似乎只是编译器的错误。
因此,标准与您的结论相符,只是没有编译器实现该解释。
2.2 编译器错误票证
已经有关于gcc和clang的此问题的已打开bug:
2.3 为什么这些年后还没有解决?
我不知道,但考虑到他们现在必须每3年左右实现一个新的标准,并且每次对语言进行大量更改,似乎有理由忽略大多数程序员可能永远不会遇到的问题。
请注意,这只是原始类型的问题。我猜测出现这个错误的原因是因为对于那些类型,由于左值到右值的转换规则,可以通过static_cast
/ reinterpret_cast
来删除cv限定符。
如果T是非类类型,则prvalue的类型是T的无cv限定符版本。否则,prvalue的类型为T。
请注意,此错误仅影响非类类型,对于类类型,它将完美地工作:
struct B { int i; };
struct D : B {};
D d;
d.i = 12;
B const& ref = d;
D& k = (D&)ref;
每个编译器都会存在一些未正确实现的边缘情况,如果这让你感到困扰,你可以提供修复方法,也许它们会在下一个版本中合并(至少适用于clang和gcc)。
2.4 gcc代码分析
在gcc中,C风格的转换目前通过cp_build_c_cast
解决:
tree cp_build_c_cast(location_t loc, tree type, tree expr, tsubst_flags_t complain) {
tree value = expr;
tree result;
bool valid_p;
result = build_const_cast_1 (loc, type, value, complain & tf_warning,
&valid_p);
if (valid_p)
{
if (result != error_mark_node)
{
maybe_warn_about_useless_cast (loc, type, value, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
}
return result;
}
result = build_static_cast_1 (loc, type, value, true,
&valid_p, complain);
if (!valid_p)
result = build_reinterpret_cast_1 (loc, type, value, true,
&valid_p, complain);
if (valid_p
&& !error_operand_p (result))
{
tree result_type;
maybe_warn_about_useless_cast (loc, type, value, complain);
maybe_warn_about_cast_ignoring_quals (loc, type, complain);
if (!CLASS_TYPE_P (type))
type = TYPE_MAIN_VARIANT (type);
result_type = TREE_TYPE (result);
if (!CLASS_TYPE_P (result_type) && !TYPE_REF_P (type))
result_type = TYPE_MAIN_VARIANT (result_type);
if (!same_type_p (non_reference (type), non_reference (result_type)))
{
result = build_const_cast_1 (loc, type, result, false, &valid_p);
gcc_assert (valid_p);
}
return result;
}
return error_mark_node;
}
实现基本上是这样的:
- 尝试使用
const_cast
- 尝试使用
static_cast
(在暂时忽略潜在的 const 不匹配的情况下)
- 尝试使用
reinterpret_cast
(在暂时忽略潜在的 const 不匹配的情况下)
- 如果在
static_cast
或 reinterpret_cast
变体中存在 const 不匹配,则在其前面添加 const_cast
。
所以由于某种原因,在这种情况下 build_static_cast_1
没有成功,因此 build_reinterpret_cast_1
执行了它的任务(这将导致由于严格别名规则而产生未定义行为)。
float
转换为int
不等同于将对float
的引用转换为对int
的引用。无论引用是否带有 const 限定符都是不相关的。 - Sam Varshavchikconst
是必要的,以允许static_cast
链条正常工作;你需要构造一个临时变量。 - ecatmurfloat f;
,(int&)
使用reinterpret_cast
进行转换,但是对于一个const float & f
,希望使用static_cast
+const_cast
进行转换,因为在第二种情况下,f
是一个引用类型? - François Andrieux