I'm trying to understand the implementation of std::is_class. I've copied some possible implementations and compiled them, hoping to figure out how they work. That done, I find that all the computations are done during compilation (as I should have figured out sooner, looking back), so gdb can give me no more detail on what exactly is going on.
The implementation I'm struggling to understand is this one:
template<class T, T v>
struct integral_constant{
static constexpr T value = v;
typedef T value_type;
typedef integral_constant type;
constexpr operator value_type() const noexcept {
return value;
}
};
namespace detail {
template <class T> char test(int T::*); //this line
struct two{
char c[2];
};
template <class T> two test(...); //this line
}
//Not concerned about the is_union<T> implementation right now
template <class T>
struct is_class : std::integral_constant<bool, sizeof(detail::test<T>(0))==1
&& !std::is_union<T>::value> {};
I'm having trouble with the two commented lines. This first line:
template<class T> char test(int T::*);
What does the T::* mean? Also, is this not a function declaration? It looks like one, yet this compiles without defining a function body.
The second line I want to understand is:
template<class T> two test(...);
Once again, is this not a function declaration with no body ever defined? Also what does the ellipsis mean in this context? I thought an ellipsis as a function argument required one defined argument before the ...?
I would like to understand what this code is doing. I know I can just use the already implemented functions from the standard library, but I want to understand how they work.
References:
std::is_class
std::integral_constant
What you are looking at is some programming technologie called "SFINAE" which stands for "Substitution failure is not an error". The basic idea is this:
namespace detail {
template <class T> char test(int T::*); //this line
struct two{
char c[2];
};
template <class T> two test(...); //this line
}
This namespace provides 2 overloads for test(). Both are templates, resolved at compile time. The first one takes a int T::* as argument. It is called a Member-Pointer and is a pointer to an int, but to an int thats a member of the class T. This is only a valid expression, if T is a class.
The second one is taking any number of arguments, which is valid in any case.
So how is it used?
sizeof(detail::test<T>(0))==1
Ok, we pass the function a 0 - this can be a pointer and especially a member-pointer - no information gained which overload to use from this.
So if T is a class, then we could use both the T::* and the ... overload here - and since the T::* overload is the more specific one here, it is used.
But if T is not a class, then we cant have something like T::* and the overload is ill-formed. But its a failure that happened during template-parameter substitution. And since "substitution failures are not an error" the compiler will silently ignore this overload.
Afterwards is the sizeof() applied. Noticed the different return types? So depending on T the compiler chooses the right overload and therefore the right return type, resulting in a size of either sizeof(char) or sizeof(char[2]).
And finally, since we only use the size of this function and never actually call it, we dont need an implementation.
Part of what is confusing you, which isn't explained by the other answers so far, is that the test functions are never actually called. The fact they have no definitions doesn't matter if you don't call them. As you realised, the whole thing happens at compile time, without running any code.
The expression sizeof(detail::test<T>(0)) uses the sizeof operator on a function call expression. The operand of sizeof is an unevaluated context, which means that the compiler doesn't actually execute that code (i.e. evaluate it to determine the result). It isn't necessary to call that function in order to know the sizeof what the result would be if you called it. To know the size of the result the compiler only needs to see the declarations of the various test functions (to know their return types) and then to perform overload resolution to see which one would be called, and so to find what the sizeof the result would be.
The rest of the puzzle is that the unevaluated function call detail::test<T>(0) determines whether T can be used to form a pointer-to-member type int T::*, which is only possible if T is a class type (because non-classes can't have members, and so can't have pointers to their members). If T is a class then the first test overload can be called, otherwise the second overload gets called. The second overload uses a printf-style ... parameter list, meaning it accepts anything, but is also considered a worse match than any other viable function (otherwise functions using ... would be too "greedy" and get called all the time, even if there's a more specific function t hat matches the arguments exactly). In this code the ... function is a fallback for "if nothing else matches, call this function", so if T isn't a class type the fallback is used.
It doesn't matter if the class type really has a member variable of type int, it is valid to form the type int T::* anyway for any class (you just couldn't make that pointer-to-member refer to any member if the type doesn't have an int member).
The std::is_class type trait is expressed through a compiler intrinsic (called __is_class on most popular compilers), and it cannot be implemented in "normal" C++.
Those manual C++ implementations of std::is_class can be used in educational purposes, but not in a real production code. Otherwise bad things might happen with forward-declared types (for which std::is_class should work correctly as well).
Here's an example that can be reproduced on any msvc x64 compiler.
Suppose I have written my own implementation of is_class:
namespace detail
{
template<typename T>
constexpr char test_my_bad_is_class_call(int T::*) { return {}; }
struct two { char _[2]; };
template<typename T>
constexpr two test_my_bad_is_class_call(...) { return {}; }
}
template<typename T>
struct my_bad_is_class
: std::bool_constant<sizeof(detail::test_my_bad_is_class_call<T>(nullptr)) == 1>
{
};
Let's try it:
class Test
{
};
static_assert(my_bad_is_class<Test>::value == true);
static_assert(my_bad_is_class<const Test>::value == true);
static_assert(my_bad_is_class<Test&>::value == false);
static_assert(my_bad_is_class<Test*>::value == false);
static_assert(my_bad_is_class<int>::value == false);
static_assert(my_bad_is_class<void>::value == false);
As long as the type T is fully defined by the moment my_bad_is_class is applied to it for the first time, everything will be okay. And the size of its member function pointer will remain what it should be:
// 8 is the default for such simple classes on msvc x64
static_assert(sizeof(void(Test::*)()) == 8);
However, things become quite "interesting" if we use our custom type trait with a forward-declared (and not yet defined) type:
class ProblemTest;
The following line implicitly requests the type int ProblemTest::* for a forward-declared class, definition of which cannot be seen by the compiler right now.
static_assert(my_bad_is_class<ProblemTest>::value == true);
This compiles, but, unexpectedly, breaks the size of a member function pointer.
It seems like the compiler attempts to "instantiate" (similarly to how templates are instantiated) the size of a pointer to ProblemTest's member function in the same moment that we request the type int ProblemTest::* within our my_bad_is_class implementation. And, currently, the compiler cannot know what it should be, thus it has no choice but to assume the largest possible size.
class ProblemTest // definition
{
};
// 24 BYTES INSTEAD OF 8, CARL!
static_assert(sizeof(void(ProblemTest::*)()) == 24);
The size of a member function pointer was trippled! And it cannot be shrunk back even after the definition of class ProblemTest has been seen by the compiler.
If you work with some third party libraries that rely on particular sizes of member function pointers on your compiler (e.g., the famous FastDelegate by Don Clugston), such unexpected size changes caused by some call to a type trait might be a real pain. Primarily because type trait invocations are not supposed to modify anything, yet, in this particular case, they do -- and this is extremely unexpected even for an experienced developer.
On the other hand, had we implemented our is_class using the __is_class intrinsic, everything would have been OK:
template<typename T>
struct my_good_is_class
: std::bool_constant<__is_class(T)>
{
};
class ProblemTest;
static_assert(my_good_is_class<ProblemTest>::value == true);
class ProblemTest
{
};
static_assert(sizeof(void(ProblemTest::*)()) == 8);
Invocation of my_good_is_class<ProblemTest> does not break any sizes in this case.
So, my advice is to rely on the compiler intrinsics when implementing your custom type traits like is_class wherever possible. That is, if you have a good reason to implement such type traits manually at all.
What does the T::* mean? Also, is this not a function declaration? It looks like one, yet this compiles without defining a function body.
The int T::* is a pointer to member object. It can be used as follows:
struct T { int x; }
int main() {
int T::* ptr = &T::x;
T a {123};
a.*ptr = 0;
}
Once again, is this not a function declaration with no body ever defined? Also what does the ellipsis mean in this context?
In the other line:
template<class T> two test(...);
the ellipsis is a C construct to define that a function takes any number of arguments.
I would like to understand what this code is doing.
Basically it's checking if a specific type is a struct or a class by checking if 0 can be interpreted as a member pointer (in which case T is a class type).
Specifically, in this code:
namespace detail {
template <class T> char test(int T::*);
struct two{
char c[2];
};
template <class T> two test(...);
}
you have two overloads:
one that is matched only when a T is a class type (in which case this one is the best match and "wins" over the second one)
on that is matched every time
In the first the sizeof the result yields 1 (the return type of the function is char), the other yields 2 (a struct containing 2 chars).
The boolean value checked is then:
sizeof(detail::test<T>(0)) == 1 && !std::is_union<T>::value
which means: return true only if the integral constant 0 can be interpreted as a pointer to member of type T (in which case it's a class type), but it's not a union (which is also a possible class type).
Test is an overloaded function that either takes a pointer to member in T or anything. C++ requires that the best match be used. So if T is a class type it can have a member in it...then that version is selected and the size of its return is 1. If T is not a class type then T::* make zero sense so that version of the function is filtered out by SFINAE and won't be there. The anything version is used and it's return type size is not 1. Thus checking the size of the return of calling that function results in a decision whether the type might have members...only thing left is making sure it's not a union to decide if it's a class or not.
Here is standard wording:
[expr.sizeof]:
The sizeof operator yields the number of bytes occupied by a non-potentially-overlapping object of the type of its operand.
The operand is either an expression, which is an unevaluated operand
([expr.prop])......
2. [expr.prop]:
In some contexts, unevaluated operands appear ([expr.prim.req], [expr.typeid], [expr.sizeof], [expr.unary.noexcept], [dcl.type.simple], [temp]).
An unevaluated operand is not evaluated.
3. [temp.fct.spec]:
[Note: Type deduction may fail for the following reasons:
...
(11.7) Attempting to create “pointer to member of T” when T is not a class type.
[ Example:
template <class T> int f(int T::*);
int i = f<int>(0);
— end example
]
As above shows, it is well-defined in standard :-)
4. [dcl.meaning]:
[Example:
struct X {
void f(int);
int a;
};
struct Y;
int X::* pmi = &X::a;
void (X::* pmf)(int) = &X::f;
double X::* pmd;
char Y::* pmc;
declares pmi, pmf, pmd and pmc to be a pointer to a member of X of type int, a pointer to a member of X of type void(int), a pointer to a member ofX of type double and a pointer to a member of Y of type char respectively.The declaration of pmd is well-formed even though X has no members of type double. Similarly, the declaration of pmc is well-formed even though Y is an incomplete type.
Related
I'm studying about iterator and I found some source code on github.
I realize what this code do but cannot find how.
template <class T>
struct _has_iterator_category
{
private:
struct _two { char _lx; char _lxx; };
template <class U> static _two _test(...);
template <class U> static char _test(typename U::iterator_category * = 0);
public:
static const bool value = sizeof(_test<T>(0)) == 1;
};
I think this code check if T has iterator_category, but I cannot figure few things about how and why this works.
Why this code use two template? what class U template does?
Is _test(...) function or constructor? And what is (...) means?
2-1. If _test is function, is this code doing function overloading? then how can be overloaded with different return type?
2-2. If _test is constructor, then is char a class in c++?
What does * = operator do in (typename U::iterator_category * = 0)? Is it multiplying iterator_category or make 0 of iterator_category pointer?
what sizeof(_test<T>(0)) == 1; means? Is it return true if sizeof(_test<T>(0)) is 1?
I searched a lot of document for iterator_traits and other things, but failed to interpret this code.
First thing first, this code is completely interpretable on it's own, if you know C++. No documentation on external components is required. It's not depending on anything. You have asked questions which suggest some gaps in basic C++ syntax understanding.
1. Template definition _test is a template member of class template _has_iterator_category. It's a template defined within template, so even if you instantiate _has_iterator_category, you still have to instantiate _test later, it got a separate template parameter.
2. Technically, it's neither. Because a class template isn't a type and a function template, which _test is, is not a function.
Constructor's name always matched the most nested enclosing class scope, i.e. _has_iterator_category in here. _has_iterator_category doesn't have a constructor declared.
It's a template of function. There are two templates, for different arguments, with different argument type. If both templates can be instantiated through successful substitution of U with concrete type, the function is overloaded.
3. It's not operator * =, operators cannot have a whitespace in them. It's * and =. This is a nameless version of argument list which could be written otherwise:
template <class U> static char _test(typename U::iterator_category *arg = 0);
= 0 is default value of function parameter arg. As arg is not being used in this context, its name can be skipped.
The single parameter of function's signature got type U::iterator_category *. typename is a keyword required by most but recent C++ standards for a nested type dependant on template parameter. This assumes that U must have a nested type
iterator_category. Otherwise the substitution of template parameters would fail.
template <class U> static _two _test(...);
Here function signature is "variadic". It means that function may take any number of arguments after substitution of template parameters. Just like printf.
4. sizeof(_test<T>(0)) == 1 equals to true if size of _test<T>(0) result is equal to 1.
The whole construction is a form of rule known as SFINAE - Substitution Failure Is Not An Error. Even if compiler fails to substitute one candidate, it would still try other candidates. The error would diagnosed when all options are exhausted.
In this case the expression sizeof(_test<T>(0)), which attempts to substitute U with T. It's the reason why _test is made into a nested template. The class is valid, but now we check the function.
If type-id T::iterator_category is valid, then substitution will be successful, as the resulting declaration will be valid. _test(...) can be successful too, but then we go to overload choice rules.
A variadic argument always implies type conversion, so there is no ambiguity and _test(...) will be discarded.
If T::iterator_category is not a valid type, _two _test(...) is the only instance of _test().
Assuming that sizeof(char) equals to 1, the constant value is initialized with true if return value of expression would be _test<T>(0) got same size as char. Which is only true if T::iterator_category exists.
Essentially this constructs checks, if class T contains nested type T::iterator_category in somewhat clumsy and outdated ways. But it is compatible with very early C++ standards as it doesn't use nullptr or <type_traits> header.
Last week Eric Niebler tweeted a very compact implementation for the std::is_function traits class:
#include <type_traits>
template<int I> struct priority_tag : priority_tag<I - 1> {};
template<> struct priority_tag<0> {};
// Function types here:
template<typename T>
char(&is_function_impl_(priority_tag<0>))[1];
// Array types here:
template<typename T, typename = decltype((*(T*)0)[0])>
char(&is_function_impl_(priority_tag<1>))[2];
// Anything that can be returned from a function here (including
// void and reference types):
template<typename T, typename = T(*)()>
char(&is_function_impl_(priority_tag<2>))[3];
// Classes and unions (including abstract types) here:
template<typename T, typename = int T::*>
char(&is_function_impl_(priority_tag<3>))[4];
template <typename T>
struct is_function
: std::integral_constant<bool, sizeof(is_function_impl_<T>(priority_tag<3>{})) == 1>
{};
But how does it work?
The general idea
Instead of listing all the valid function types, like the sample implementation over on cpprefereence.com, this implementation lists all of the types that are not functions, and then only resolves to true if none of those is matched.
The list of non-function types consists of (from bottom to top):
Classes and unions (including abstract types)
Anything that can be returned from a function (including void and reference types)
Array types
A type that does not match any of those non-function types is a function type. Note that std::is_function explicitly considers callable types like lambdas or classes with a function call operator as not being functions.
is_function_impl_
We provide one overload of the is_function_impl function for each of the possible non-function types. The function declarations can be a bit hard to parse, so let's break it down for the example of the classes and unions case:
template<typename T, typename = int T::*>
char(&is_function_impl_(priority_tag<3>))[4];
This line declares a function template is_function_impl_ that takes a single argument of type priority_tag<3> and returns a reference to an array of 4 chars. As is customary since the ancient days of C, the declaration syntax gets horribly convoluted by the presence of array types.
This function template takes two template arguments. The first is just an unconstrained T, but the second is a pointer to a member of T of type int. The int part here does not really matter, ie. this will even work for Ts that do not have any members of type int. What it does though is that it will result in a syntax error for Ts that are not of class or union type. For those other types, attempting to instantiate the function template will result in a substitution failure.
Similar tricks are used for the priority_tag<2> and priority_tag<1> overloads, which use their second template arguments to form expressions that only compile for Ts being valid function return types or array types respectively. Only the priority_tag<0> overload does not have such a constraining second template parameter and thus can be instantiated with any T.
All in all we declare four different overloads for is_function_impl_, which differ by their input argument and return type. Each of them takes a different priority_tag type as argument and returns a reference to a char array of different unique size.
Tag dispatching in is_function
Now, when instantiating is_function, it instantiates is_function_impl with T. Note that since we provided four different overloads for this function, overload resolution has to take place here. And since all of these overloads are function templates, that means SFINAE has a chance to kick in.
So for functions (and only functions) all of the overloads will fail except the most general one with priority_tag<0>. So why doesn't instantiation always resolve to that overload, if it's the most general one? Because of the input arguments of our overloaded functions.
Note that priority_tag is constructed in such a way that priority_tag<N+1> publicly inherits from priority_tag<N>. Now, since is_function_impl is invoked here with priority_tag<3>, that overload is a better match than the others for overload resolution, so it will be tried first. Only if that fails due to a substitution error the next-best match is tried, which is the priority_tag<2> overload. We continue in this way until we either find an overload that can be instantiated or we reach priority_tag<0>, which is not constrained and will always work. Since all of the non-function types are covered by the higher prio overloads, this can only happen for function types.
Evaluating the result
We now inspect the size of the type returned by the call to is_function_impl_ to evaluate the result. Remember that each overload returns a reference to a char array of different size. We can therefore use sizeof to check which overload was selected and only set the result to true if we reached the priority_tag<0> overload.
Known Bugs
Johannes Schaub found a bug in the implementation. An array of incomplete class type will be incorrectly classified as a function. This is because the current detection mechanism for array types does not work with incomplete types.
I know that sizeof operator doesn't evaluate its expression argument to get the answer. But it is not one of the non-deducted contexts for templates. So I am wondering how it interacts with templates and specifically template argument deductions. For instance, the following is taken from C++ Templates: The Complete Guide:
template<typename T>
class IsClassT {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int C::*);
template<typename C> static Two test(...);
public:
enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
This type function determines, as its name suggests, whether a template argument is a class type. The mechanism is essentially the following condition test:
sizeof(IsClassT<T>::test<T>(0)) == 1
Note, however, the function template argument is explicit (T in this case) and the function argument is a plan 0 of type int, which is not of type pointer to an int member of class C. In normal function template argument deduction, when T is really of class type and function argument is simply a 0, deduction on static One test(int C::*); should fail since implicit conversion (0 used as null pointer type) is not allowed during template argument deduction and (I guess?) SFINAE should kick in and overload resolution would have selected
static Two test(...);
However, since the whole expression is wrapped inside the sizeof operator, it seems that passing the 0 without a cast works.
Can someone clarify:
if my understanding of function template argument deduction is correct?
if it is because of the non-evaluation nature of sizeof operator that makes passing 0 successful? And
if 0 doesn't matter in this context, we could choose any argument in place of 0, such as 0.0, 100 or even user defined types?
Conclusion: I found in C++ Primer that has a section on function template explicit arguments. And I quote "Normal Conversions Apply for Explicitly Specified Arguments" and "For the same reasons that normal conversions are permitted for parameters that
are defined using ordinary types (§ 16.2.1, p. 680), normal conversions also apply
for arguments whose template type parameter is explicitly specified". So the 0 in this question is actually implicitly converted to null pointer to members (pointer conversion).
Template Argument Deduction is done when instantiating a function. This is done as part of function overloading (and other contexts not applicable here). In TAD, the types of function arguments are used to deduce the template parameters, but not all arguments are necessarily used. This is where the "non-deduced context" comes from. If a template parameter appears in a non-deduced context within a function signature, it can't be deduced from the actual argument.
sizeof(T) is in fact a non-deduced context for T, but it's so obvious that nobody even bothered to mention it. E.g.
template< int N> class A {};
template<typename T> void f(A<sizeof(T)>);
f(A<4>());
The compiler isn't going to pick a random T that has sizeof(T)==4.
Now your example actually doesn't have a sizeof inside the argument list of a function template, so "non-deduced context" is an irrelevant consideration. That said, it's important to understand what "sizeof doesn't evaluate its expression argument" means. It means the expression value isn't calculated, but the expression type is. In your example, IsClassT<T>::test<T>(0) won't be called at runtime, but its type is determined at compile time.
The friend of mine send me an interesting task:
template<typename T>
class TestT
{
public:
typedef char ONE;
typedef struct { char a[2]; } TWO;
template<typename C>
static ONE test(int C::*);
template<typename C>
static TWO test(...);
public:
enum{ Yes = sizeof(TestT<T>::template test<T>(0)) == 1 };
enum{ No = !Yes };
};
I can't compile this code with VS2013. With GCC 4.9.0 it compiles. But I can't understand what it does.
The points of interest for me:
How it can work if functions have a declaration only but no definition?
What the TestT<T>::template test<T>(0) is? It looks like a function call.
What is this ::template means?
What's purpose of class above?
How is used principle called?
int C::* is a pointer to a int member, right?
It does not actually call the functions, it just looks at what the sizeof of the return type would be.
It is a function call. See below.
The template is necessary because of the dependent type problem.
It tests if there can be a pointer to a data member of the type parameter. This is true for class types only (e.g. for std::string but not for int). You can find code like this for example here which includes something very similar to your example - under the name of is_class.
SFINAE, which stands for "Substitution Failure Is Not An Error". The reason for this name becomes obvious once you realize that the substitution of C for int will fail and thus simply cause one of the function overloads to not exist (instead of causing a compiler error and aborting compilation).
Yes, it is a pointer that points to an int inside of an object of type C.
That's too many questions for a single question, but nevertheless:
sizeof doesn't evaluate its operand, it only determines the type. That doesn't require definitions for any functions called by the operand - the type is determined from the declaration.
Yes, that's a function call. If the class and function weren't templates, it would look like TestT::test(0).
template is needed because the meaning of the name test depends on the class template parameter(s), as described in Where and why do I have to put the "template" and "typename" keywords?.
It defines a constant Yes which is one if T is a class type and zero otherwise. Also a constant No with the logically inverted value.
It looks like it's intended for use in SFINAE, to allow templates to be partially specialised for class and non-class types. Since C++11, we can use standard traits like std::is_class for this purpose.
Yes, if C is a class type. Otherwise, it's a type error, so the overload taking that type is ignored, leaving just the second overload. Thus, the return type of test is ONE (with size one) if C is a class type, and TWO (with size two) otherwise; so the test for sizeof(...) == 1 distinguishes between class and non-class types.
The class template ::std::numeric_limits<T> may only be instantiated for types T, which can be the return value of functions, since it always defines member functions like static constexpr T min() noexcept { return T(); } (see http://www.cplusplus.com/reference/limits/numeric_limits/ for more information of the non-specialised versions in c++03 or c++11).
If T is i.e. int[2] the instantiation will immediately lead to a compile time error, since int[2] cannot be the return value of a function.
Wrapping ::std::numeric_limits with a safe version is easy - if a way to determine if it is safe to instantiate ::std::numeric_limits is known. This is necessary, since the problematic functions should be accessible if possible.
The obvious (and obviously wrong) way of testing ::std::numeric_limits<T>::is_specialised does not work since it requires instantiation of the problematic class template.
Is there a way to test for safety of instantiation, preferably without enumerating all known bad types? Maybe even a general technique to determine if any class template instantiation is safe?
Concerning the type trait that decides whether a type can be returned for a function, here is how I would go about it:
#include <type_traits>
template<typename T, typename = void>
struct can_be_returned_from_function : std::false_type { };
template<typename T>
struct can_be_returned_from_function<T,
typename std::enable_if<!std::is_abstract<T>::value,
decltype(std::declval<T()>(), (void)0)>::type>
: std::true_type { };
On the other hand, as suggested by Tom Knapen in the comments, you may want to use the std::is_arithmetic standard type trait to determine whether you can specialize numeric_limits for a certain type.
Per paragraph 18.3.2.1/2 of the C++11 Standard on the numeric_limits class template, in fact:
Specializations shall be provided for each arithmetic type, both floating point and integer, including bool.
The member is_specialized shall be true for all such specializations of numeric_limits.
C++11 solutions
Since T only appears as the return type of static member functions in the declarations of the unspecialised ::std::numeric_limits<T> (see C++03 18.2.1.1 and C++11 18.3.2.3), it is enough for this specific problem to ensure that doing so is declaration-safe.
The reason this leads to a compile time error is, that the use of a template-argument may not give rise to an ill-formed construct in the instantiation of the template specialization (C++03 14.3/6, C++11 14.3/6).
For C++11 enabled projects, Andy Prowl's can_be_returned_from_function solution works in all relevant cases: http://ideone.com/SZB2bj , but it is not easily portable to a C++03 environment. It causes an error in when instantiated with an incomplete type ( http://ideone.com/k4Y25z ). The proposed solution will accept incomplete classes instead of causing an error. The current Microsoft compiler (msvc 1700 / VS2012) seems to dislike this solution and fail to compile.
Jonathan Wakely proposed a solution that works by utilizing std::is_convertible<T, T> to determine if T can be the return value of a function. This also eliminates incomplete classes, and is easy to show correct (it is defined in C++11 to do exactly what we want). Execution shows that all cases (arrays, arrays of undefined length, functions, abstract classes) which are known to be problematic are correctly recognized. As a bonus, it also correctly recognizes incomplete classes, which are not allowed as parameters to numeric_limits by the standards (see below), although they seem to cause no problems in practice, as long as no problematic functions are actually called. Test execution: http://ideone.com/zolXpp . Some current compilers (icc 1310 and msvc 1700, which is VS2012's compiler) generate incorrect results with this method.
Tom Knapen's is_arithmetic solution is a very concise C++11 solution, but requires the implementer of a type that specialises numeric_limits to also specialise is_arithmetic. Alternatively, a type that in its base case inherits from is_arithmetic (this type might be called numeric_limits_is_specialised) might be specialised in those cases, since specialising is_abstract might not be semantically correct (e.g. a type that does not specify all basic arithmetic operators, but still is a valid integer-like type).
This whitelisting approach ensures that even incomplete types are handled correctly, unless someone maliciously tries to force compilation errors.
Caveat
As shown by the mixed results, C++11 support remains spotty, even with current compilers, so your mileage with these solutions may vary. A C++03 solution will benefit from more consistent results and the ability to be used in projects that do not wish to switch to C++11.
Towards a robust C++03 solution
Paragraph C++11 8.3.5/8 lists the restrictions for return values:
If the type of a parameter includes a type of the form "pointer to array of unknown bound of T" or "reference to array of unknown bound of T", the program is ill-formed. Functions shall not have a return type of type array or function, although they may have a return type of type pointer or reference to such things. There shall be no arrays of functions, although there can be arrays of pointers to functions.
and goes on in paragraph C++11 8.3.5/9:
Types shall not be defined in return or parameter types. The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).
Which is pretty much the same as paragraph C++03 8.3.5/6:
If the type of a parameter includes a type of the form "pointer to array of unknown bound of T" or "reference to array of unknown bound of T", the program is ill-formed. Functions shall not have a return type of type array or function, although they may have a return type of type pointer or reference to such things. There shall be no arrays of functions, although there can be arrays of pointers to functions. Types shall not
be defined in return or parameter types. The type of a parameter or the return type for a function definition shall not be an incomplete class type (possibly cv-qualified) unless the function definition is nested within the member-specification for that class (including definitions in nested classes defined within the class).
Another kind of problematic types is mentioned identically in C++11 10.4/3 and C++03 10.4/3:
An abstract class shall not be used as a parameter type, as a function return type, or as the type of an explicit conversion. [...]
The problematic functions are not nested within an incomplete class type (except of ::std::numeric_limits<T>, which cannot be their T), so we have four kinds of problematic values of T: Arrays, functions, incomplete class types and abstract class types.
Array Types
template<typename T> struct is_array
{ static const bool value = false; };
template<typename T> struct is_array<T[]>
{ static const bool value = true; };
template<typename T, size_t n> struct is_array<T[n]>
{ static const bool value = true; };
detects the simple case of T being an array type.
Incomplete Class Types
Incomplete class types interestingly do not lead to a compilation error just from instantiation, which means either the tested implementations are more forgiving than the standard, or I am missing something.
C++03 example: http://ideone.com/qZUa1N
C++11 example: http://ideone.com/MkA0Gr
Since I cannot come up with a proper way to detect incomplete types, and even the standard specifies (C++03 17.4.3.6/2 item 5)
In particular, the effects are undefined in the following cases: [...] if an incomplete type (3.9) is used as a template argument when instantiating a template component.
Adding only the following special allowance in C++11 (17.6.4.8/2):
[...] unless specifically allowed for that component
it seems safe to assume that anybody passing incomplete types as template parameters are on their own.
A complete list of the cases where C++11 allows incomplete type parameters is quite short:
declval
unique_ptr
default_delete (C++11 20.7.1.1.1/1: "The class template default_delete serves as the default deleter (destruction policy) for the class template unique_ptr."
shared_ptr
weak_ptr
enable_shared_from_this
Abstract Class & Function Types
Detecting functions is a bit more work than in C++11, since we do not have variadic templates in C++03. However, the above quotes on functions already contain the hint we need; functions may not be elements of arrays.
Paragraph C++11 8.3.4\1 contains the sentence
T is called the array element type; this type shall not be a reference type, the (possibly cv qualified) type void, a function type or an abstract class type.
which is also verbatim in paragraph C++03 8.3.4\1 and will allow us to test if a type is a function type. Detecting (cv) void and reference types is simple:
template<typename T> struct is_reference
{ static const bool value = false; };
template<typename T> struct is_reference<T&>
{ static const bool value = true; };
template<typename T> struct is_void
{ static const bool value = false; };
template<> struct is_void<void>
{ static const bool value = true; };
template<> struct is_void<void const>
{ static const bool value = true; };
template<> struct is_void<void volatile>
{ static const bool value = true; };
template<> struct is_void<void const volatile>
{ static const bool value = true; };
Using this, it is simple to write a meta function for abstract class types and functions:
template<typename T>
class is_abstract_class_or_function
{
typedef char (&Two)[2];
template<typename U> static char test(U(*)[1]);
template<typename U> static Two test(...);
public:
static const bool value =
!is_reference<T>::value &&
!is_void<T>::value &&
(sizeof(test<T>(0)) == sizeof(Two));
};
Note that the following meta function may be used to distinguish between the two, should one wish to make a distinct is_function and is_abstract_class
template<typename T>
class is_class
{
typedef char (&Two)[2];
template<typename U> static char test(int (U::*));
template<typename U> static Two test(...);
public:
static const bool value = (sizeof(test<T>(0)) == sizeof(char));
};
Solution
Combining all of the previous work, we can construct the is_returnable meta function:
template<typename T> struct is_returnable
{ static const bool value = !is_array<T>::value && !is_abstract_class_or_function<T>::value; };
Execution for C++03 (gcc 4.3.2): http://ideone.com/thuqXY
Execution for C++03 (gcc 4.7.2): http://ideone.com/OR4Swf
Execution for C++11 (gcc 4.7.2): http://ideone.com/zIu7GJ
As expected, all test cases except for the incomplete class yield the correct answer.
In addition to the above test runs, this version is tested (with the exact same test program) to yield the same results w/o warnings or errors on:
MSVC 1700 (VS2012 with and w/o XP profile), 1600 (VS2010), 1500 (VS2008)
ICC Win 1310
GCC (C++03 and C++11/C++0x mode) 4.4.7, 4.6.4, 4.8.0 and a 4.9 snapshot
Restrictions for either case
Note that, while this approach in either version works for any numeric_limits implementation that does not extend upon the implementation shown in the standard, it is by no means a solution to the general problem, and in fact may theoretically lead to problems with weird but standard compliant implementations (e.g. ones which add private members).
Incomplete classes remain a problem, but it seems silly to require higher robustness goals than the standard library itself.
std::is_convertible<T, T>::value will tell you if a type can be returned from a function.
is_convertible<T1, T2> is defined in terms of a function returning a T2 converted from an expression of type T1.
#include <limits>
#include <type_traits>
struct Incomplete;
struct Abstract { virtual void f() = 0; };
template<typename T>
using is_numeric_limits_safe = std::is_convertible<T, T>;
int main()
{
static_assert(!is_numeric_limits_safe<Incomplete>::value, "Incomplete");
static_assert(!is_numeric_limits_safe<Abstract>::value, "Abstract");
static_assert(!is_numeric_limits_safe<int[2]>::value, "int[2]");
}
This might not be exactly what you want, because it is safe to instantiate std::numeric_limits<Incomplete> as long as you don't call any of the functions that return by value. It's not possible to instantiate std::numeric_limits<int[2]> though.
Here's a better test (using SFINAE) which gives is_numeric_limits_safe<Incomplete>::value==true
template<typename T>
class is_numeric_limits_unsafe
{
struct mu { };
template<typename U>
static U test(int);
template<typename U>
static mu test(...);
public:
typedef std::is_same<decltype(test<T>(0)), mu> type;
};
template<typename T>
struct is_numeric_limits_safe
: std::integral_constant<bool, !is_numeric_limits_unsafe<T>::type::value>
{ };