一、为什么考虑到这个问题
在看std::tr1的function实现时,看到一个问题。比方说在C++中可以通过指针获得一个变量的位置
tsecer@harry: cat function.cpp
#include <tr1/functional>
using namespace std::tr1;
int main()
{
function<void(void*)> f;
if (f)
{
return 0;
}
}
tsecer@harry: g++ -c function.cpp
tsecer@harry:
这个地方的f其实只是一个自定义的结构,但是它可以放在if表达式中直接使用,这一点也是很神奇的。
二、网络上关于这个的说明
大致说来:conversion operator把类转换为指定的TypeName类型
In general, member functions of the form
operator TypeName()
(with optional explicit
and const
qualifier) are conversion operators. It allows you to cast your class to any type specified by TypeName
.
另一方面,构造函数允许将其它类型转换为该类型
In the other direction, constructors with one argument allow you to cast any type to your class:
class Foo {
Foo(int x); // convert int to Foo
operator bool() const; // convert Foo to bool
int x;
};
This defines implicit conversions for your class. The compiler tries to apply these conversions if possible (like what it does for built-in data types, e.g. 5 + 1.0
). You can declare them to be explicit
to suppress unwanted implicit conversions.
三、gcc中对于该功能的实现
1、对于该类型函数的解析
tree
mangle_conv_op_name_for_type (const tree type)
{
void **slot;
tree identifier;
if (conv_type_names == NULL)
conv_type_names = htab_create_ggc (31, &hash_type, &compare_type, NULL);
slot = htab_find_slot_with_hash (conv_type_names, type,
(hashval_t) TYPE_UID (type), INSERT);
identifier = (tree)*slot;
if (!identifier)
{
char buffer[64];
/* Create a unique name corresponding to TYPE. */
sprintf (buffer, "operator %lu",
(unsigned long) htab_elements (conv_type_names));
identifier = get_identifier (buffer);
*slot = identifier;
/* Hang TYPE off the identifier so it can be found easily later
when performing conversions. */
TREE_TYPE (identifier) = type;
/* Set bits on the identifier so we know later it's a conversion. */
IDENTIFIER_OPNAME_P (identifier) = 1;
IDENTIFIER_TYPENAME_P (identifier) = 1;
}
return identifier;
}
2、判断一个节点是否为转换操作符的宏
gcc-4.1.0gcccpcp-tree.h
/* Nonzero if NODE is a user-defined conversion operator. */
#define DECL_CONV_FN_P(NODE)
(DECL_NAME (NODE) && IDENTIFIER_TYPENAME_P (DECL_NAME (NODE)))
3、当需要一个特定类型时对一个对象所有转换操作符的查找
gcc-4.1.0gcccpsearch.c
static int
lookup_conversion_operator (tree class_type, tree type)
{
……
/* All the conversion operators come near the beginning of
the class. Therefore, if FN is not a conversion
operator, there is no matching conversion operator in
CLASS_TYPE. */
fn = OVL_CURRENT (fn);
if (!DECL_CONV_FN_P (fn))
break;
4、从类型到if表达式的转换
在C++的规范中说明了,if表达式中需要的是bool值,所以这里尝试对if中的表达式进行隐世转换。由于编译器知道每个类的所有转换操作符,所以根据这些操作符的返回值选择一个合适的操作符即可。
(gdb) bt
#0 build_user_type_conversion_1 (totype=0xb7d064ac, expr=0xb7d02370, flags=8)
at ../../gcc-4.1.0/gcc/cp/call.c:2519
#1 0x0804baec in implicit_conversion (to=0xb7d064ac, from=0xb7d9a844,
expr=0xb7d02370, c_cast_p=0 ' 00', flags=3)
at ../../gcc-4.1.0/gcc/cp/call.c:1249
#2 0x0805573e in perform_implicit_conversion (type=0xb7d064ac,
expr=0xb7d02370) at ../../gcc-4.1.0/gcc/cp/call.c:6348
#3 0x080bdf28 in condition_conversion (expr=0xb7d02370)
at ../../gcc-4.1.0/gcc/cp/typeck.c:3756
#4 0x080d509c in maybe_convert_cond (cond=0xb7d02370)
at ../../gcc-4.1.0/gcc/cp/semantics.c:573
#5 0x080d51d1 in finish_if_stmt_cond (cond=0xb7d02370, if_stmt=0xb7d7c0f0)
at ../../gcc-4.1.0/gcc/cp/semantics.c:632
#6 0x080a56be in cp_parser_selection_statement (parser=0xb7d9bed4)
at ../../gcc-4.1.0/gcc/cp/parser.c:6362
#7 0x080a5114 in cp_parser_statement (parser=0xb7d9bed4,
in_statement_expr=0x0) at ../../gcc-4.1.0/gcc/cp/parser.c:6052
#8 0x080a55c4 in cp_parser_statement_seq_opt (parser=0xb7d9bed4,
in_statement_expr=0x0) at ../../gcc-4.1.0/gcc/cp/parser.c:6307
#9 0x080a554f in cp_parser_compound_statement (parser=0xb7d9bed4,
in_statement_expr=0x0, in_try=0 ' 00')
at ../../gcc-4.1.0/gcc/cp/parser.c:6280
#10 0x080abdeb in cp_parser_function_body (parser=0xb7d9bed4)
---Type <return> to continue, or q <return> to quit---
at ../../gcc-4.1.0/gcc/cp/parser.c:12345
#11 0x080abe14 in cp_parser_ctor_initializer_opt_and_function_body (
parser=0xb7d9bed4) at ../../gcc-4.1.0/gcc/cp/parser.c:12362
#12 0x080af284 in cp_parser_function_definition_after_declarator (
parser=0xb7d9bed4, inline_p=0 ' 00')
at ../../gcc-4.1.0/gcc/cp/parser.c:15258
#13 0x080af196 in cp_parser_function_definition_from_specifiers_and_declarator
(parser=0xb7d9bed4, decl_specifiers=0xbffff188, attributes=0x0,
declarator=0x8713774) at ../../gcc-4.1.0/gcc/cp/parser.c:15202
#14 0x080aa7a7 in cp_parser_init_declarator (parser=0xb7d9bed4,
decl_specifiers=0xbffff188, function_definition_allowed_p=1 ' 01',
member_p=0 ' 00', declares_class_or_enum=0,
function_definition_p=0xbffff183 " 01")
at ../../gcc-4.1.0/gcc/cp/parser.c:10898
#15 0x080a679b in cp_parser_simple_declaration (parser=0xb7d9bed4,
function_definition_allowed_p=1 ' 01')
at ../../gcc-4.1.0/gcc/cp/parser.c:7181
#16 0x080a6652 in cp_parser_block_declaration (parser=0xb7d9bed4,
statement_p=0 ' 00') at ../../gcc-4.1.0/gcc/cp/parser.c:7081
#17 0x080a64cc in cp_parser_declaration (parser=0xb7d9bed4)
at ../../gcc-4.1.0/gcc/cp/parser.c:6998
#18 0x080a61d8 in cp_parser_declaration_seq_opt (parser=0xb7d9bed4)
at ../../gcc-4.1.0/gcc/cp/parser.c:6897
---Type <return> to continue, or q <return> to quit---
#19 0x080a14dc in cp_parser_translation_unit (parser=0xb7d9bed4)
at ../../gcc-4.1.0/gcc/cp/parser.c:2682
#20 0x080b23f8 in c_parse_file () at ../../gcc-4.1.0/gcc/cp/parser.c:17566
#21 0x0812708a in c_common_parse_file (set_yydebug=0)
at ../../gcc-4.1.0/gcc/c-opts.c:1143
#22 0x08441d5a in compile_file () at ../../gcc-4.1.0/gcc/toplev.c:991
#23 0x0844337e in do_compile () at ../../gcc-4.1.0/gcc/toplev.c:1949
#24 0x084433e0 in toplev_main (argc=2, argv=0xbffff494)
at ../../gcc-4.1.0/gcc/toplev.c:1981
#25 0x08130ebf in main (argc=2, argv=0xbffff494)
at ../../gcc-4.1.0/gcc/main.c:35
(gdb)
四、C++中对于基本类型标准promotion和conversion的说明
1、关于safe bool的说明
The safe bool problem
Until the introduction of explicit conversion functions in C++11, designing a class that should be usable in boolean contexts (e.g. if(obj) { ... }) presented a problem: given a user-defined conversion function, such as T::operator bool() const;, the implicit conversion sequence allowed one additional standard conversion sequence after that function call, which means the resultant bool could be converted to int, allowing such code asobj << 1; or int i = obj;.
One early solution for this can be seen in std::basic_ios, which defines operator! and operator void*(until C++11), so that the code such as if(std::cin) {...} compiles because void* is convertible to bool, but int n = std::cout; does not compile because void* is not convertible to int. This still allows nonsense code such as delete std::cout; to compile, and many pre-C++11 third party libraries were designed with a more elaborate solution, known as the Safe Bool idiom.
2、一个测试
从这个测试可以看到,感觉同样是转换到bool,感觉是double转换为bool的优先级比指针转换为bool的优先级更高。
tsecer@harry: cat double.vs.pointer.cpp
#include <stdio.h>
struct A
{
operator double () {printf("double
"); return 3.14f;}
operator void * () {printf("void *
"); return (void*)0;}
};
int main()
{
A a;
bool y = a;
}
tsecer@harry: g++ double.vs.pointer.cpp
tsecer@harry: ./a.out
double
3、为什么double到bool的转换高于void*到bool的转换
标准说明,指针到bool类型的转换优先级最低。
From here, we learn something important: Pointer conversions and boolean conversions have the same rank. Remember that as we head to Ranking Implicit Conversion Sequences (§13.3.3.2 [over.ics.rank]).
Looking at /4, we see:
Standard conversion sequences are ordered by their ranks: an Exact Match is a better conversion than a Promotion, which is a better conversion than a Conversion. Two conversion sequences with the same rank are indistinguishable unless one of the following rules applies:
— A conversion that does not convert a pointer, a pointer to member, or std::nullptr_t to bool is better than one that does.
We've found our answer in the form of a very explicit statement. Hooray!
4、gcc中对于该代码的实现
gcc-4.1.0gcccpcall.c
static conversion *
standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
int flags)
{
……
else if (tcode == BOOLEAN_TYPE)
{
/* [conv.bool]
An rvalue of arithmetic, enumeration, pointer, or pointer to
member type can be converted to an rvalue of type bool. */
if (ARITHMETIC_TYPE_P (from)
|| fcode == ENUMERAL_TYPE
|| fcode == POINTER_TYPE
|| TYPE_PTR_TO_MEMBER_P (from))
{
conv = build_conv (ck_std, to, conv);
if (fcode == POINTER_TYPE
|| TYPE_PTRMEM_P (from)
|| (TYPE_PTRMEMFUNC_P (from)
&& conv->rank < cr_pbool))
conv->rank = cr_pbool;
return conv;
}
return NULL;
}
……
}
gcc-4.1.0gcccpcall.c
/* The rank of the conversion. Order of the enumerals matters; better
conversions should come earlier in the list. */
typedef enum conversion_rank {
cr_identity,
cr_exact,
cr_promotion,
cr_std,
cr_pbool,
cr_user,
cr_ellipsis,
cr_bad
} conversion_rank;
5、int到bool和double到bool转换的优先级
在从int到bool vs double到bool的转换过程中,两者rank相同,此时出现二义性。
tsecer@harry: cat double.vs.pointer.cpp
#include <stdio.h>
struct A
{
operator int () {printf("int *
"); return 0;}
operator double () {printf("double
"); return 3.14f;}
//operator void * () {printf("void *
"); return (void*)0;}
};
int main()
{
A a;
bool y = a;
}
tsecer@harry: g++ double.vs.pointer.cpp
double.vs.pointer.cpp: In function ‘int main()’:
double.vs.pointer.cpp:15: 错误:从‘A’到‘bool’的转换有歧义
double.vs.pointer.cpp:7: 附注:备选为: A::operator double()
double.vs.pointer.cpp:6: 附注: A::operator int()
tsecer@harry:
五、回到原始的问题
在tr1中function的conversion operation的定义位置为:
gcc-4.1.0libstdc++-v3include r1functional_iterate.h
template<typename _Res _GLIBCXX_COMMA _GLIBCXX_TEMPLATE_PARAMS>
class function<_Res(_GLIBCXX_TEMPLATE_ARGS)>
#if _GLIBCXX_NUM_ARGS == 1
: public unary_function<_T1, _Res>, private _Function_base
#elif _GLIBCXX_NUM_ARGS == 2
: public binary_function<_T1, _T2, _Res>, private _Function_base
#else
: private _Function_base
#endif
{
……
// [3.7.2.3] function capacity
/**
* @brief Determine if the %function wrapper has a target.
*
* @return @c true when this %function object contains a target,
* or @c false when it is empty.
*
* This function will not throw an exception.
*/
operator _Safe_bool() const
{
if (_M_empty())
{
return 0;
}
else
{
return &_Hidden_type::_M_bool;
}
}
……
};