I'm trying tow write a SFINAE template to determine whether two classes can be added together. This is mostly to better understand how SFINAE works, rather than for any particular "real world" reason.
So what I've come up with is
#include <assert.h>
struct Vec
{
Vec operator+(Vec v );
};
template<typename T1, typename T2>
struct CanBeAdded
{
struct One { char _[1]; };
struct Two { char _[2]; };
template<typename W>
static W make();
template<int i>
struct force_int { typedef void* T; };
static One test_sfinae( typename force_int< sizeof( make<T1>() + make<T2>() ) >::T );
static Two test_sfinae( ... );
enum { value = sizeof( test_sfinae( NULL ) )==1 };
};
int main()
{
assert((CanBeAdded<int, int>::value));
assert((CanBeAdded<int, char*>::value));
assert((CanBeAdded<char*, int>::value));
assert((CanBeAdded<Vec, Vec>::value));
assert((CanBeAdded<char*, int*>::value));
}
This compiles for all except the last line, which gives
finae_test.cpp: In instantiation of ‘CanBeAdded<char*, int*>’:
sfinae_test.cpp:76: instantiated from here
sfinae_test.cpp:40: error: invalid operands of types ‘char*’ and ‘int*’ to binary ‘operator+’
So this error is kind-of what I'd expect, but I'd expect the compiler to then find the test_sfinae( ... ) definition and use that instead (and not complain about the one that doesn't parse.
Clearly I'm missing something, I just don't know what it is.
It looks to me like you've run into the problem that's discussed in Core Issue 339 as well as N2634. The bottom line is that you're pushing a bit beyond what any compiler can currently handle, even though what you're doing is allowed by the standard. C++ 0x will add more detail about what will and won't result in SFINAE failure versus a hard error. See N3000, §14.9.2, if you want to get into the gory details.
Related
I have a number of places in which I wish to use std::enable_if to allow certain templates only if a simple static cast from template type A to template type B (both of which are numeric) will not result in any loss of data. However I am not sure what existing type traits, if any, I should use or if I should write my own.
For example, casting from uint16_t to uint32_t, from float to double, even from int to double is not going to lose any precision or negative sign. But casting from double to int or int to uint32_t would obviously be problematic.
I've monkeyed around a bit, testing std::is_trivially_constructible, std::is_assignable, std::is_constructible, etc etc. but I don't see one that will warn me if I try to go from float to int.
Am I missing something that's in the library currently or should I just write it myself?
(I already know how to write it. It's simple. Just want to make sure I don't reinvent the wheel).
I am answering my own question this because someone asked me to post my trait and comments don't seem to have formatting.
template <class T, class F>
struct is_safe_numeric_conversion
: pred_base <( ( ( ( std::is_integral<T>::value && std::is_integral<F>::value ) || ( std::is_floating_point<T>::value && std::is_floating_point<F>::value ) ) &&
sizeof(T) >= sizeof(F) ) ||
( std::is_floating_point<T>::value && std::is_integral<F>::value ) ) &&
( ( std::is_signed<T>::value && std::is_signed<F>::value ) || ( std::is_unsigned<T>::value && std::is_unsigned<F>::value ) )>
{
};
Some notes about why I did what I did here:
I ended up using sizeof to check the actual sizes of types instead of numeric_limits::max / lowest. I don't like that and would have preferred to use numeric_limits but Visual C++ was giving me fits about that. I'm wondering of perhaps it's because their constexpr implementation doesn't work in some of the versions I'm using.
I use my own little "pred_base" just to make things less verbose. I realize I could have used integral_constant for that
Just today I realized that this disallows a valid conversion from a small unsigned type (say, uint8_t) to a large signed signed type (say int64_t) even though the latter can easily hold all possible values of the former. I need to fix that but it's minor and at this point, I think I'm the only one still interested in this...
FINAL VERSION (edited 3-FEB-2018)
StackOverflow tells me that someone just gave me points for this today. So I guess people might actually be using it. In that case, I suppose I should present my entire, current version which addresses the flaws I mentioned above.
I'm sure there are better ways to do this and I know C++14/17/etc allow me to do this far less verbosely but I was forced to make this work on VS versions all the way back to VS2012 so I couldn't take advantage of alias templates and the like.
Therefore I did this by writing some helper traits and then composed my final "is_safe_numeric_cast" trait from them. I think it makes things more readable.
// pred_base selects the appropriate base type (true_type or false_type) to
// make defining our own predicates easier.
template<bool> struct pred_base : std::false_type {};
template<> struct pred_base<true> : std::true_type {};
// same_decayed
// -------------
// Are the decayed versions of "T" and "O" the same basic type?
// Gets around the fact that std::is_same will treat, say "bool" and "bool&" as
// different types and using std::decay all over the place gets really verbose
template <class T, class O>
struct same_decayed
: pred_base <std::is_same<typename std::decay<T>::type, typename std::decay<O>::type>::value>
{};
// is_numeric. Is it a number? i.e. true for floats and integrals but not bool
template<class T>
struct is_numeric
: pred_base<std::is_arithmetic<T>::value && !same_decayed<bool, T>::value>
{
};
// both - less verbose way to determine if TWO types both meet a single predicate
template<class A, class B, template<typename> class PRED>
struct both
: pred_base<PRED<A>::value && PRED<B>::value>
{
};
// Some simple typedefs of both (above) for common conditions
template<class A, class B> struct both_numeric : both<A, B, is_numeric> { }; // Are both A and B numeric types?
template<class A, class B> struct both_floating : both<A, B, std::is_floating_point> { }; // Are both A and B floating point types?
template<class A, class B> struct both_integral : both<A, B, std::is_integral> { }; // Are both A and B integral types
template<class A, class B> struct both_signed : both<A, B, std::is_signed> { }; // Are both A and B signed types
template<class A, class B> struct both_unsigned : both<A, B, std::is_unsigned> { }; // Are both A and B unsigned types
// Returns true if both number types are signed or both are unsigned
template<class T, class F>
struct same_signage
: pred_base<(both_signed<T, F>::value) || (both_unsigned<T, F>::value)>
{
};
// And here, finally is the trait I wanted in the first place: is_safe_numeric_cast
template <class T, class F>
struct is_safe_numeric_cast
: pred_base <both_numeric<T, F>::value && // Obviously both src and dest must be numbers
( std::is_floating_point<T>::value && ( std::is_integral<F>::value || sizeof(T) >= sizeof(F) ) ) || // Floating dest: src must be integral or smaller/equal float-type
( ( both_integral<T, F>::value ) && // Integral dest: src must be integral and (smaller/equal+same signage) or (smaller+different signage)
( sizeof(T) > sizeof(F) || ( sizeof(T) == sizeof(F) && same_signage<T, F>::value ) ) )>
{
};
One possible solution leveraging SFINAE (C++17 required for std::void_t):
namespace detail
{
template<typename From, typename To, typename = void>
struct is_narrowing_conversion_impl : std::true_type {};
template<typename From, typename To>
struct is_narrowing_conversion_impl<From, To, std::void_t<decltype(To{std::declval<From>()})>> : std::false_type {};
} // namespace detail
template<typename From, typename To>
struct is_narrowing_conversion : detail::is_narrowing_conversion_impl<From, To> {};
Narrowing conversion rules are implicity available with brace initialization. The compiler will report an error when a narrowing cast is required for initialization, e.g. uint8_t{int(1337)}.
The expression decltype(To{std::declval<From>()}) in is_narrowing_conversion_impl is ill formed in case of a narrowing cast and will result in the correct value being set for is_narrowing_conversion::value:
// all following assertions hold:
static_assert(!is_narrowing_conversion<std::int8_t, std::int16_t>::value);
static_assert(!is_narrowing_conversion<std::uint8_t, std::int16_t>::value);
static_assert(!is_narrowing_conversion<float, double>::value);
static_assert( is_narrowing_conversion<double, float>::value);
static_assert( is_narrowing_conversion<int, uint32_t>::value);
Tested with clang, gcc and msvc
Example: godbolt
I think the header <limits> gives you the primitives you'll need to build the full traits.
Here's a trait to check whether one integral would narrow when converted to another (of similar signed-ness):
#include <iostream>
#include <limits>
template<class IntFrom, class IntTo> static constexpr auto WouldNarrow = std::numeric_limits<IntFrom>::max() > std::numeric_limits<IntTo>::max();
int main()
{
using namespace std;
cout << WouldNarrow<int, short> << endl;
return 0;
}
I was having this same problem and the answer given by #Quxflux helped greatly. Here is his approach of using the brace initialization rules adapted to a C++20 concept:
template <typename From, typename To>
concept is_narrowing_conversion = !requires(From from)
{
To{from};
};
Usage:
// all following assertions hold:
static_assert(!is_narrowing_conversion<std::int8_t, std::int16_t>);
static_assert(!is_narrowing_conversion<std::uint8_t, std::int16_t>);
static_assert(!is_narrowing_conversion<float, double>);
static_assert( is_narrowing_conversion<double, float>);
static_assert( is_narrowing_conversion<int, uint32_t>);
godbolt
I have a struct that manages 'views' into an array of variable type. The purpose of this is to provide a unified state vector for ODE simulation while at the same time working on individual segments of this vector from several other classes in an organized fashion. If this triggers a design pattern in your mind, please let me know.
My issue is that the first implementation ContainerHolderFirst, using Cont::pointer does not compile for const arrays.
My next attempt with std::conditional, mixing in Cont::const_pointer still doesn't work.
Only the third attempt with std::conditional and modification of Cont::value_type compiles (and seems to work in my larger project).
My questions are the following:
It would be nice if ContainerHolderFirst would work. I'd wish that const-ness of the type would be propagated to the pointer. Why isn't it?
I understand even less why ContainerHolderSecond doesn't work. The explanation in https://stackoverflow.com/a/1647394/1707931 rather suggests that this is the way to go, no? It does actually work. My bad.
Are there issues with the third approach that I haven't uncovered yet? Is there a simpler way?
Full C++11 code follows:
Update1: Fixing ContainerHolderSecond. It does compile with correct initialization. Also added ContainerHolderBarry suggested by Barry using decltype and declval.
This leaves the question whether any of the approaches are preferred? Will they lead to performance differences? They should all compile to the same object, no?
#include <iostream>
#include <array>
template <typename Cont>
class ContainerHolderFirst {
Cont& data_;
const static size_t offset_ = 1;
typename Cont::pointer data_view;
public:
ContainerHolderFirst(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
template <typename Cont>
class ContainerHolderSecond {
using Pointer = typename std::conditional<std::is_const<Cont>::value,
typename Cont::const_pointer,
typename Cont::pointer>::type;
Cont& data_;
const static size_t offset_ = 1;
Pointer data_view;
public:
ContainerHolderSecond(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
template <typename Cont>
class ContainerHolderBarry {
using Pointer = decltype(&std::declval<Cont&>()[0]);
Cont& data_;
const static size_t offset_ = 1;
Pointer data_view;
public:
ContainerHolderBarry(Cont& data) : data_(data), data_view(&data[offset_]) {}
};
int main() {
using namespace std;
array<int, 2> my_array;
ContainerHolderFirst<array<int, 2>> holder(my_array); // works
const array<int, 2> const_array{5,7};
// ContainerHolderFirst<const array<int, 2>> const_holder(const_array);
/* error: invalid conversion from 'const value_type* {aka const int*}' to 'std::array<int, 2ull>::pointer {aka int*}' [-fpermissive] */
ContainerHolderSecond<array<int,2>> second_holder(my_array); // works!
ContainerHolderSecond<const array<int,2>> const_holder(const_array); //updated; works as well; awkward
ContainerHolderThird<array<int,2>> third_holder(my_array); // still works
ContainerHolderThird<const array<int,2>> third_const_holder(const_array); //finally compiles as well
ContainerHolderBarry<array<int,2>> barry_holder(my_array);
ContainerHolderBarry<const array<int,2>> barry_const_holder(const_array);
}
You're making this unnecessarily difficult on yourself. If you want the type of &cont[offset], just ask for the type of that expression. Use std::declval along with decltype:
template <typename Cont>
class ContainerHolder {
using Pointer = decltype(&std::declval<Cont&>()[0]);
...
};
The only problem with ContainerHolderSecond is that you're using it incorrectly:
ContainerHolderSecond<array<int,2>> const_holder(const_array);
// ^-- insert "const" here
As for ContainerHolderFirst, the reason that array<T, N>::pointer is the same type as (array<T, N> const)::pointer is that there is no automatic way to determine where the const qualification should be added to the nested type, and there is no language facility to describe this (that is, we don't have const-qualified typedefs or type aliases).
I have some code that uses two different type of colours, 8 bit per channel and 16 bit per channel, each represented by a struct. In order to effectively reuse my code I have a template function that does some rendering with them. I would therefore like a templated function to grab the max value of a channel of my colours.
My initial attempt looked like this. I have only shown the specialization for 8 bpc
struct Pixel8
{
unsigned char r;
unsigned char g;
unsigned char b;
};
#define PIXEL8_MAX 255
template <class PIXEL>
auto getMax( ) -> decltype( PIXEL::r )
{
static_assert( sizeof(PIXEL) > 0, "getMax can only be called with a pixel type." );
}
template <>
auto getMax<Pixel8>( ) -> decltype( Pixel8::r )
{
return PIXEL8_MAX;
}
This would not compile with Visual studio 2012. I get the error
1> error C2785: ''unknown-type' getMax(void)' and 'char getMax(void)' have different return types
1> see declaration of 'getMax'
To me I feel that this should work but I have been unable to find any examples. There is one other question similar at Specialize function template with decltype trailing return type, but here the return type is the same for each specialization.
I have found a workaround which I will post as an answer so that others can benefit. However it is not very transparent so if someone can tell me if the code above is valid and this is a VC++ incompatibility or if it is not valid then why and how I can make it valid?
Try to make the return type depend on the template parameter type:
struct Pixel8
{
char r;
char g;
char b;
};
template<typename T>
struct ColourType
{
typedef decltype(T::r) type;
};
#define PIXEL8_MAX 255
template <class PIXEL>
typename ColourType<PIXEL>::type getMax()
{
static_assert(false, "getMax can only be called with a pixel type.");
}
template <>
ColourType<Pixel8>::type getMax<Pixel8>()
{
return PIXEL8_MAX;
}
This is a workaround for getting the required behaviour. It relies on the fact that there is another way to define a type in C++ without using decltype. This is to use typname.
It's main use in this context is as follows, imagine two classes and two functions
class MyClass
{
public:
class MyThing
{
};
};
class MyOtherClass
{
public:
static int MyThing;
}
template< class T >
void func1( T something )
{
typename T::MyThing thing;
}
template< class T >
void func2( T something )
{
T::MyThing = 5;
}
If we pass either class as a template parameter T, then T::MyThing would be a type for MyClass and a static int for MyOtherClass. These are entirely incompatible, so we use typename to separate them.
In func1 we use typename to state that T::MyThing is a type. We could pass in a MyClass object. In func2 we omit typename and therefore T::MyThing is interpreted as a variable and we could pass in a MyOtherClass. Without typename, there would be no way to tell if T::MyThing was a type or a static variable.
Note also that typename can refer to a typdef as well as an internal class, So if we create a templated class or struct which includes a typedef for a type we can access that type using typename.
template<class PIXEL>
struct pixTypes
{
};
template<>
struct pixTypes<Pixel8>
{
typedef char type;
};
template <class PIXEL>
auto getMax( ) -> typename pixTypes<PIXEL>::type
{
static_assert( false, "getMax can only be called with a pixel type." );
}
template <>
auto getMax<Pixel8>() -> typename pixTypes<Pixel8>::type
{
return PIXEL8_MAX;
}
So now we get our return type from a typename which refers to a typedef in a specialized templated struct.
This seems rather a convoluted way around everything, but it does compile on Visual Studio 12.
Using macros to define constants, as in this question's code
#define PIXEL8_MAX 255
… is not ideal.
Also, the definition conflicts with the type used. A char is not guaranteed to have that maximum value, and with most implementations will by default not have that maximum value. You can define a Byte type as unsigned char, but even so you're not guaranteed 8 bits, and should check that.
The standard library provides the numeric_limits class template to deal with maximum values etc.:
#include <limits> // std::numeric_limits
#define STATIC_ASSERT( e ) static_assert( e, #e )
using Byte = unsigned char;
int const bits_per_byte = std::numeric_limits<Byte>::digits;
STATIC_ASSERT( bits_per_byte == 8 );
struct Pixel8
{
Byte r;
Byte g;
Byte b;
};
template< class Pixel >
constexpr auto getMax() -> decltype( Pixel::r )
{
return std::numeric_limits<decltype( Pixel::r )>::max();
}
#include <iostream>
using namespace std;
auto main() -> int
{
cout << +getMax<Pixel8>() << endl;
}
After seeing many examples of metaprogramming in C++ that allow for figuring out may properties of classes (such as knowing if a type is a specialization of a template ), or knowing if a class incorporates a given nested type; but I was wondering if is was possible to write a test or trait that determines the inverse of the last one - to check if a given Type is nested within a class or struct.
In other words, I'm looking for the equivalent of the following pseudocode:
template <typename Type> struct is_nested {
enum { value = {__some magic__} };
};
typedef int type1;
struct Something { typedef int internal_type; };
typedef Something::internal_type type2;
//...later, likely at a different scope
is_nested< int >::value; // yields false
is_nested< std::vector<int>::iterator >::value; // yields true
is_nested< type1 >::value; // yields false
is_nested< type2 >::value; // yields true
I know I can use sizeof to implement yes/no tests, and I presume Type is part of those tests, but I can't figure out how to plug in some sort of "any viable type" into the test such that I can form an expression like Anytype::Type.
template
struct is_nested
{
typedef char yes;
typedef struct { char u[2]; } no;
// Herein lies the problem
???? static yes test( char [ sizeof(Anytype::Type) ] ) ;
???? static no test(...);
public:
enum { value = sizeof(test(0)) == sizeof(char) };
};
(Note that I don't care nor (can afford to) know what type would Type be nested in; all it matters is if it is nested in something or not. In other words, this trait should only depend on Type.)
I'm looking for a C++ solution be it in C++11 or C++03, but in the first case I would welcome it much more if it was backportable.
What you are asking is not possible, but not due to a technical limitation, rather because you can't always tell whether a type name identifies a nested type or not - and templates work with types, not names.
In this case, for instance:
is_nested< std::vector<int>::iterator >::value
You do not know what iterator is. Consider this class my_vector:
template<typename T>
struct my_vector
{
typedef T* iterator;
// ...
};
What should is_nested<my_vector<int>::iterator>::value yield? You probably expect the result to be true.
However, what is nested here is the alias, not the type itself: the type int* is not nested. In fact, I expect you would wish the following to yield false:
is_nested<int*>::value
So here the same is_nested<T> trait should yield two different results given the same type T (int*, in this case). The information based on which is_nested<> should define value cannot be retrieved from the type T itself - and templates work with types, not names.
It may be possible to check if the "canonical type" (the result type after all aliases are resolved) is nested using non-standard features of compilers.
CTTI can get the name of a type at compile time. Then find : in the string:
#include <vector>
#include "ctti/type_id.hpp"
constexpr bool has_colon(const ctti::detail::string& s, size_t i) {
return i < s.length() ? (s[i] == ':' || has_colon(s, i + 1)) : false;
}
template<typename T>
using is_nested = integral_constant<bool, has_colon(ctti::type_id<T>().name(), 0)>;
typedef int type1;
struct Something { typedef int internal_type; };
typedef Something::internal_type type2;
static_assert(!is_nested< int >::value, "");
static_assert(is_nested< std::vector<int>::iterator >::value, "");
static_assert(!is_nested< type1 >::value, "");
// static_assert(is_nested< type2 >::value, ""); // fail
The 4th check will fail because type2 is just int, which is not nested.
I used the code from "Is there a way to test whether a C++ class has a default constructor (other than compiler-provided type traits)?".
I modified it slightly to work with all my test cases:
template< class T >
class is_default_constructible {
typedef int yes;
typedef char no;
// the second version does not work
#if 1
template<int x, int y> class is_equal {};
template<int x> class is_equal<x,x> { typedef void type; };
template< class U >
static yes sfinae( typename is_equal< sizeof U(), sizeof U() >::type * );
#else
template<int x> class is_okay { typedef void type; };
template< class U >
static yes sfinae( typename is_okay< sizeof U() >::type * );
#endif
template< class U >
static no sfinae( ... );
public:
enum { value = sizeof( sfinae<T>(0) ) == sizeof(yes) };
};
Why does it work correctly with the two template argument version but not with the normal one (set #if 0)?
Is this a compiler bug? I'm using Visual Studio 2010.
I used the following tests:
BOOST_STATIC_ASSERT( is_default_constructible<int>::value );
BOOST_STATIC_ASSERT( is_default_constructible<bool>::value );
BOOST_STATIC_ASSERT( is_default_constructible<std::string>::value );
BOOST_STATIC_ASSERT( !is_default_constructible<int[100]>::value );
BOOST_STATIC_ASSERT( is_default_constructible<const std::string>::value );
struct NotDefaultConstructible {
const int x;
NotDefaultConstructible( int a ) : x(a) {}
};
BOOST_STATIC_ASSERT( !is_default_constructible<NotDefaultConstructible>::value );
struct DefaultConstructible {
const int x;
DefaultConstructible() : x(0) {}
};
BOOST_STATIC_ASSERT( is_default_constructible<DefaultConstructible>::value );
I'm really at a loss here:
Two tests are failing with the other version: int[100] and NotDefaultConstructible. All the tests succeed with the two template argument version.
Visual Studio 2010 does not support std::is_default_constructible. However, my question is about why there is any difference in the two implementations and why one works and the other does not.
(My answer is greatly informed by DS's previous answer.)
First of all, notice that you have class is_okay { typedef void type; }, i.e., type is a private member of is_okay. This means that it's not actually visible outside the class and therefore
template< class U >
static yes sfinae( typename is_equal< sizeof U(), sizeof U() >::type * );
will never succeed. However, SFINAE didn't originally apply to this situation in C++98; it wasn't until the resolution to DR 1170 that "access checking [started being] done as part of the substitution process".[1]
(Amazingly, Paolo Carlini wrote that blog entry just 10 days ago, so your timing with this question is impeccable. In cases like this, according to Carlini, GCC prior to 4.8 didn't do access checking during SFINAE at all. So that explains why you didn't see GCC complaining about the private-ness of type. You'd have to be using a top-of-tree GCC from literally less than two weeks ago, in order to see the correct behavior.)
Clang (top-of-tree) follows the DR in -std=c++11 mode, but gives the expected error in its default C++03 mode (i.e. Clang doesn't follow the DR in C++03 mode). This is slightly odd but maybe they do it for backwards compatibility.
But anyway, you don't actually want type to be private in the first place. What you meant to write is struct is_equal and struct is_okay.
With this change, Clang passes all of your test cases. GCC 4.6.1 passes all of your test cases too, except for int[100]. GCC thinks that int[100] is okay, whereas you're asserting that it's not okay.
But another problem with your code is that it doesn't test what you think it's testing. The C++ standard, clause 8.5#10, says very clearly: [2]
An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.
So when you write sizeof U(), you're not testing if U can be default-initialized; you're testing if it can be value-initialized!
...Or are you? At least in some of my test cases, GCC's error messages indicated that U() was being interpreted as the name of a type — "function returning U" — and that was why int[100] behaved differently. I don't see how that behavior is valid, but I really don't understand the syntactic subtleties here.
If you really mean to test default initialization, you should use something like sizeof *new U everywhere you currently have sizeof U().
By the way, int[100] is default-initializable, period. The Standard is clear on what it means to default-initialize an array type.
Finally, I wonder if one cause of wacky behavior in your code is that you're trying to pass an unadorned 0 (which is of type int) to a function whose set of overloads includes one function taking void * and one taking .... I could totally understand if a compiler picked the wrong one in that case. You'd be better advised to try passing 0 to a function taking int.
Putting it all together, here's a version of your code that works perfectly for me (i.e., no assertion-failures) in both ToT Clang and GCC 4.6.1.
template< class T >
class is_default_constructible {
typedef int yes;
typedef char no;
template<int x> struct is_okay { typedef int type; };
template< class U >
static yes sfinae( typename is_okay< sizeof (*new U) >::type );
template< class U >
static no sfinae( ... );
public:
enum { value = sizeof( sfinae<T>(0) ) == sizeof(yes) };
};
#if __has_feature(cxx_static_assert)
#define BOOST_STATIC_ASSERT(x) static_assert(x, "or fail")
#else
#define dummy2(line) dummy ## line
#define dummy(line) dummy2(line)
#define BOOST_STATIC_ASSERT(x) int dummy(__COUNTER__)[(x) - 1]
#endif
#include <string>
BOOST_STATIC_ASSERT( !is_default_constructible<int()>::value );
BOOST_STATIC_ASSERT( is_default_constructible<bool>::value );
BOOST_STATIC_ASSERT( is_default_constructible<std::string>::value );
BOOST_STATIC_ASSERT( is_default_constructible<int[100]>::value );
BOOST_STATIC_ASSERT( is_default_constructible<const std::string>::value );
struct NotDefaultConstructible {
const int x;
NotDefaultConstructible( int a ) : x(a) {}
};
BOOST_STATIC_ASSERT( !is_default_constructible<NotDefaultConstructible>::value );
struct DefaultConstructible {
const int x;
DefaultConstructible() : x(0) {}
};
BOOST_STATIC_ASSERT( is_default_constructible<DefaultConstructible>::value );
This seems almost certainly an artifact (bug) of the compiler, since g++ behaves (and fails) differently. I can only guess at why VS behaves differently, but one guess that seems reasonable is that this class:
template<int x> class is_okay { typedef void type; };
has the same definition regardless of the template parameter, so perhaps the compiler skips a step when analyzing static sfinae( typename is_okay< sizeof U() >::type * ); and considers it well-defined without looking closely at the parameter of is_okay. So then it thinks everything is default-constructible.
Why neither VS nor g++ is bothered by is_okay::type being private, I don't know. Seems like they both should be.
g++, on the other hand, treats both versions as equivalent. In both, however, it gives a different error for int[100]. That one is debatable as to whether it should be default-constructible. You seem to think it shouldn't be. g++47's std::is_default_constructible thinks it is! To get that behavior (which is likely more standard), you can replace T with typename boost::remove_all_extents<T>::type in the enum line.