Remembering that T(a) can cause dangerous conversion like a C-style cast

An essential question: Should boost::lambda::constructor<int>()("") compiles? It does compile with Boost 1.47.0 . But I think it shouldn't. I have crafted a general replacement of T(a) as a workaround for dangerous conversions caused by T(a) which may happen in some kinds of templates. http://ideone.com/iZE1Z unary_initialized<T>(a).value And I thought of that the above workaround (or something similar) may be suitable to be provided within boost::initialized . Now I want to share the idea and to hear interests or evaluations in the community about such extension and underlying issues, before starting further acts. Here is the background story: A while ago, I had a conversation about dangerous conversion that can be caused by the following expression with variadic templates in C++11. T(std::forward<Args>(args)...) This expression is meant to obtain a prvalue of type T which is direct initialized from any given argument list. But if a single argument was given, this becomes T(std::forward<Arg>(arg)) and further, for example, may become int("") and compiles as reinterpret_cast which is likely unexpected. Here is a relevant quote from the standard 5.2.3 p1:
... If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4). ... (Note: 5.2.3 describes the behavior of T(a), and 5.4 describes the behavior of (T)a.) To avoid the unexpected conversion, I first proposed use of T{...} . But it is not a perfect solution because it changes the meaning in some cases like std::vector<int> . After that, I crafted a tricky workaround. http://ideone.com/FLoDk pack_initialized<T>(std::forward<Args>(args)...).value
The above story was about C++11. But it revealed that similar dangerous conversion can be also formed in C++98, C++03 as T(a) in some kinds of templates. Searching in the boost directory (trunk r75213), I found some occurrences of the problem: boost/lambda/construct.hpp:37: http://www.boost.org/doc/libs/1_47_0/boost/lambda/construct.hpp return T(a1); boost/xpressive/regex_actions.hpp:632: http://www.boost.org/doc/libs/1_47_0/boost/xpressive/regex_actions.hpp return T(a0); As shown in the top of this message, I picked boost::lambda::constructor as a concrete example, and verified that the problem really exists in the wild. Thinking more, I wonder why such dangerous conversion was explicitly injected to T(a) in the first place. I first thought of some C-compatibility issues. But now I think it cannot be, because T(a) is a new construct in C++. Is it possible to remove this dangerous conversion path in a future revision of the standard? Perhaps I should go to comp.std.c++ about the last question. -- k_satoda

Hi,
An essential question: Should boost::lambda::constructor<int>()("") compiles?
Definitely no.
I have crafted a general replacement of T(a) as a workaround for dangerous conversions caused by T(a) which may happen in some kinds of templates.
There is already one in boost (Functional/Factory) AFAIK, but it might has the same problem: http://www.boost.org/doc/libs/1_47_0/libs/functional/factory/doc/html/index.... boost::value_factory<T>()(arg1,arg2,arg3) // same as T(arg1,arg2,arg3)
To avoid the unexpected conversion, I first proposed use of T{...} . But it is not a perfect solution because it changes the meaning in some cases like std::vector<int>. But this is exactly the use-case T{...} was made for. Of course initializer_lists make this approach completely unusable...
Another possible solution would be to use static_cast<T>(a) for one constructor-argument, as static_cast is type-safe.
As shown in the top of this message, I picked boost::lambda::constructor as a concrete example, and verified that the problem really exists in the wild.
Not if lambda::constructor was well written, but it's hard to take care of all these pitfalls (typical C++).
Thinking more, I wonder why such dangerous conversion was explicitly injected to T(a) in the first place. I first thought of some C-compatibility issues. But now I think it cannot be, because T(a) is a new construct in C++. Is it possible to remove this dangerous conversion path in a future revision of the standard?
T(a) is a function-style cast which was used in C, removing it would be rather difficult. HTH michi7x7

michi7x7 wrote:
I have crafted a general replacement of T(a) as a workaround for dangerous conversions caused by T(a) which may happen in some kinds of templates.
There is already one in boost (Functional/Factory) AFAIK, but it might has the same problem: http://www.boost.org/doc/libs/1_47_0/libs/functional/factory/doc/html/index....
boost::value_factory<T>()(arg1,arg2,arg3) // same as T(arg1,arg2,arg3)
Thank you for pointing out. It is another example of the problem. I verified that boost::value_factory<int>()("") compiles, too. My replacement (unary_initialized) is meant to be used internally to aid these library codes. For the case of value_factory, something like pack_initialized, I've shown later in the story about variadic templates, seems desired.
To avoid the unexpected conversion, I first proposed use of T{...} . But it is not a perfect solution because it changes the meaning in some cases like std::vector<int>. But this is exactly the use-case T{...} was made for. Of course initializer_lists make this approach completely unusable...
FYI, I found a somewhat related paper. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2477.pdf With pack_initialized, I imagine one can still use initializer_list constructors explicitly, like this: pack_initialized<std::vector<int>>(std::initializer_list<int>{...}).value
Another possible solution would be to use static_cast<T>(a) for one constructor-argument, as static_cast is type-safe.
Unfortunately, static_cast involves base* to derived* conversion which needs special assumption to be safe.
Thinking more, I wonder why such dangerous conversion was explicitly injected to T(a) in the first place. I first thought of some C-compatibility issues. But now I think it cannot be, because T(a) is a new construct in C++. Is it possible to remove this dangerous conversion path in a future revision of the standard?
T(a) is a function-style cast which was used in C, removing it would be rather difficult.
"function-style cast which was used in C" ? I still believe that function-style cast is new in C++, meaning that (T)a is the only syntax of casts in C. Could you please give an example of valid function-style cast in C code? -- k_satoda
participants (2)
-
Kazutoshi Satoda
-
michi7x7