Boost logo

Boost :

Subject: Re: [boost] decltype and incomplete types (was: [config] vc10 and BOOST_NO_DECLTYPE)
From: Daniel Walker (daniel.j.walker_at_[hidden])
Date: 2010-04-07 12:21:25


On Tue, Apr 6, 2010 at 11:50 PM, Eric Niebler <eric_at_[hidden]> 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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk