Search code examples
c++visual-studio-2012sfinaetype-traitsdecltype

SFINAE using decltype() ignores "private" on Visual Studio 2012


I am trying to create a metafunction which will tell me whether type T can be used in a boolean context, i.e. whether code like this

decltype(T) x = /* ... */;
if (x) { /* ... */ }

will compile.

I use some of C++11 features, but limited to what is available in VS 2012, because I want my code to work there as well.

Here's my code:

#include <type_traits> // want to use remove_reference and declval from std library

// objects of this type can be used in a boolean context
class AlmostBoolean
{
public:
    operator bool() const { return true; }
};

// should not be used in a boolean context because it requires two user-defined conversions
class RequiresTwoConversions
{
public:
    operator AlmostBoolean() const { return AlmostBoolean(); }
};

// should not be used in a boolean context because the conversion operator is private
class PrivateBoolean
{
private:
    operator bool() const { return true; }
};

namespace kinda_sorta_type_traits
{
    typedef char(&yes_type)[1];
    typedef char(&no_type) [2];

    using std::remove_reference;
    using std::declval;

    template<typename T>
    struct can_be_used_in_a_boolean_context
    {
        template<typename U>
        static yes_type test(typename remove_reference
                             <
                                 decltype( static_cast<bool>(declval<const U &>()) )
                             >::type *);

        template<typename>
        static no_type test(...);

        static const bool value = sizeof(test<T>(nullptr)) == sizeof(yes_type);
    };
}

using kinda_sorta_type_traits::can_be_used_in_a_boolean_context;

static_assert(   can_be_used_in_a_boolean_context< bool                     >::value, "");
static_assert(   can_be_used_in_a_boolean_context< bool &                   >::value, "");
static_assert(   can_be_used_in_a_boolean_context< int                      >::value, "");
static_assert(   can_be_used_in_a_boolean_context< const volatile int &     >::value, "");
static_assert(   can_be_used_in_a_boolean_context< AlmostBoolean            >::value, "");
static_assert(   can_be_used_in_a_boolean_context< AlmostBoolean &          >::value, "");
static_assert( ! can_be_used_in_a_boolean_context< RequiresTwoConversions   >::value, "");
static_assert( ! can_be_used_in_a_boolean_context< RequiresTwoConversions & >::value, "");
//#ifndef _MSC_VER
static_assert( ! can_be_used_in_a_boolean_context< PrivateBoolean           >::value, "");
//#endif

int main()
{
}

This approach with two overloads of static test() function -- one taking a pointer to something deduced from decltype(expression) (top-level reference is stripped so that I could always declare a pointer to that type), and another one taking "..." -- usually worked quite well for other similar type traits that I wrote.

But with this one, I have a problem. Visual Studio 2012 complains about the last static_assert, claiming that metafunction value for PrivateBoolean type should be true, despite code like this

int main()
{
    PrivateBoolean x;
    if (x) {
    }
}

does not compile, for obvious reasons:

error: C2248: 'PrivateBoolean::operator bool' : cannot access private member declared in class 'PrivateBoolean'

I also tested this code on GCC 4.9.1, and it worked perfectly OK there.

Is that a bug in the MSVC compiler, or am I wrong somewhere in my reasoning?

UPD:

Checked the very same code on Visual Studio 2015. The bug (I believe it was actually a bug) has been fixed. No errors. Only IntelliSense still complains that this static_assert should fail, but it's not a big deal compared to the original problem :)

But still one question remains: what workaround could be done for Visual Studio 2012 to make this type trait work correctly there?


Solution

  • Sounds very reasonable, that this is a Visual Studio 2012 bug.