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?
Sounds very reasonable, that this is a Visual Studio 2012 bug.