
On Tue, Apr 6, 2010 at 11:50 PM, Eric Niebler <eric@boostpro.com> wrote:
On 4/6/2010 6:22 PM, Daniel Walker wrote:
7.1.6.2.4 states:
"The type denoted by decltype(e) is defined as follows: ... if e is a function call (5.2.2) or an invocation of an overloaded operator (parentheses around e are ignored), decltype(e) is the return type of the statically chosen function;"
5.2.2.3 states:
"The type of the function call expression is the return type of the statically chosen function (i.e., ignoring the virtual keyword), even if the type of the function actually called is different. This type shall be a complete object type, a reference type or the type void."
So, it seems the draft requires compilers to reject code taking decltype of an incomplete type.
Ouch. The implication is that we can't sub the TR1 implementation of result_of with the C++0x implementation without causing some valid code to break. That's terrible. Let's hope this is just an oversight. I'm following up with CWG.
In the mean time, what are the chances of backing out the decltype-based implementation of boost::result_of completely (until we have a better plan)?
After considering this for a while, I believe there is an argument in support of the current decltype specification, at least with respect to result_of. result_of is supposed to give the type of the result of a function call, i.e. the type of a function call expression. A call expression cannot have an incomplete type. So, result_of cannot have an incomplete type. Perhaps, Eric has identified a need for a new type specifier, say, incomplete_decltype. If we have a complete decltype, it would make sense to have an incomplete one also. However, if we are to maintain the semantics of result_of as the type of a call expression, then result_of<>::type should be a complete type. Historically, result_of could determine the type of a call expression given a function pointer or reference but could not determine the type given a function object. To get around this problem, a protocol was specified to give users a way to advertise the result type of their function objects. However, this is a fragile affair, since the behavior of result_of depends on the user's conformance to the intention of the protocol. For example, if the user advertises a type other than the result of the call expression, such as an incomplete type, then result_of would no longer give the type of the call expression. The current draft standard resolves this problem by requiring the compiler to deduce the type of the call expression rather than relying on the user to advertise it. Consequentially, code that used the previous TR1 protocol to advertise false result types will be invalid. This is true for incomplete types, but there are other examples as well. Consider the following #include <string> #include <boost/static_assert.hpp> #include <boost/type_traits/is_same.hpp> #include <boost/utility/result_of.hpp> const char* f() { return "hello world"; } struct functor { // note: the result type and result_type are not the same typedef std::string result_type; const char* operator()() const { return f(); } }; template<class T0, class T1> void test(T0 f0, T1 f1); int main() { test(&f, functor()); } template<class T0, class T1> void test(T0 f0, T1 f1) { // in c++98 the type of x1 is not the type of f1(), // but the statements are valid. typename boost::result_of<T0()>::type x0 = f0(); typename boost::result_of<T1()>::type x1 = f1(); // the following is valid in c++0x but not c++98 BOOST_STATIC_ASSERT(( boost::is_same< typename boost::result_of<T0()>::type, typename boost::result_of<T1()>::type >::value )); } So, yes, there are examples of code that are valid in c++98 but not 0x, and vice versa, depending on how closely the type advertised through the legacy protocol matches the actual result type. I think a good solution would be for boost::result_of to offer a backwards compatible mode. So, in c++0x if the user defines, say, BOOST_LEGACY_RESULT_OF then boost::result_of will follow the old TR1 protocol. How does that sound? Daniel Walker