Use templated struct as template parameter to static constexpr member - c++

I am trying to set up a static constexpr member of a template struct to be assigned by a template parameter, which in itself is a template struct. For example:
template<int f>
struct Column
{
static constexpr int flags = f;
};
template<Column<int> c> // Error: Template argument for non-type template parameter must be an expression
struct Row
{
static constexpr Column<int> column = c; // Error: Template argument for non-type template parameter must be an expression
};
This is to be used to create other structs with information, for example:
struct Table
{
static constexpr Column<5> myCol();
static constexpr Row<myCol> myRow(); // Error: Value of type 'Column<5> ()' is not implicitly convertible to 'int'
};
Xcode gives me the errors as described in the comments. I think the idea as to what I want to achieve should be fairly clear, but how can I implement it correctly? I want the template parameter for Column<int> to be generic (i.e. not using Column<5> for example) so that any generated instances of Column<int> can be used as template argument to Row.
Edit: more context to better handle the XY problem:
I am creating templated structs that will represent SQLite columns. These template structs will have column name, flags, default values and so on. This is the Foo class above, in very simple terms.
Then I will create similar structs for SQLite rows. These structs need to know about which column they belong to to know which flags it has (NOT NULL, PRIMARY KEY and similar things), which default value it has if any, and so on. Therefore I want to pass the column struct (i.e. Foo<int>) as a template argument to the row struct (Bar in the example above).

std::integral_constant
Lets see an example:
template <std::size_t index>
class column: public std::integral_constant<std::size_t, index>
{
...
};
Then, when passing to row, or vice versa, use this:
template <std::size_t column_index>
class row
{
...
};
Now, lets use it:
row<column<1>{}> myrow;
Notice the {}, it will create an instance of column, but it will automatically decay to std::size_t!
The problem is that people still can use it directly with the index, so you might want to put a clang-tidy check in your codebase for this.
Template metaprogramming
One can also use partial specializations.
template <typename column_t>
class row;
template <std::size_t column_index>
class row<column<column_index>>
{
...
}

I think you want something like
template<int f>
struct Column
{
static constexpr int flags = f;
};
template<int i>
struct Row
{
static constexpr Column<i> col;
};
Column<int> is a non-sensical type, since it passes int to a template that is expecting a value of type int as its argument.

Related

Get number of template parameters with template template function

I'm not sure if this is possible, but I would like to count the number of template arguments of any class like:
template <typename T>
class MyTemplateClass { ... };
template <typename T, typename U>
class MyTemplateClass2 { ... };
such that template_size<MyTemplateClass>() == 1 and template_size<MyTemplateClass2>() == 2. I'm a beginner to template templates, so I came up with this function which of course does not work:
template <template <typename... Ts> class T>
constexpr size_t template_size() {
return sizeof...(Ts);
}
because Ts can not be referenced. I also know that it might come to problems when handling variantic templates, but that is not the case, at least for my application.
Thx in advance
There is one...
° Introduction
Like #Yakk pointed out in his comment to my other answer (without saying it explicitly), it is not possible to 'count' the number of parameters declared by a template. It is, on the other hand, possible to 'count' the number of arguments passed to an instantiated template.
Like my other answer shows it, it is rather easy to count these arguments.
So...
If one cannot count parameters...
How would it be possible to instantiate a template without knowing the number of arguments this template is suppose to receive ???
Note
If you wonder why the word instantiate(d) has been stricken throughout this post,
you'll find its explanation in the footnote. So keep reading... ;)
° Searching Process
If one can manage somehow to try to instantiate a template with an increasing number of arguments, and then, detect when it fails using SFINAE (Substitution Failure Is Not An Error), it should be possible to find a generic solution to this problem... Don't you think ?
Obviously, if one wants to be able to also manage non-type parameters, it's dead.
But for templates having only typename parameters...
There is one...
Here are the elements with which one should be able to make it possible:
A template class declared with only typename parameters can receive any type as argument. Indeed, although there can have specializations defined for specific types,
a primary template cannot enforce the type of its arguments.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The above statement might no longer be true from C++20 concepts.
I cannot try ATM, but #Yakk seems rather confident on the subject. After his comment:
I think concepts breaks this. "a primary template cannot enforce the type of its arguments." is false.
He might be right if constraints are apply before the template instantiation. But...
By doing a quick jump to the introduction to Constraints and concepts, one can read, after the first code example:
"Violations of constraints are detected at compile time, early in the template instantiation process, which leads to easy to follow error messages."
To be confirmed...
It is perfectly possible to create a template having for sole purpose to
be instantiated with any number of arguments. For our use case here, it might contain only ints... (let's call it IPack).
It is possible to define a member template of IPack to define the Next IPack by adding an int to the arguments of the current IPack. So that one can progressively increase its number of arguments...
Here is maybe the missing piece. It is maybe something that most people don't realize.
(I mean, most of us uses it a lot with templates when, for example, the template accesses a member that one of its arguments must have, or when a trait tests for the existence of a specific overload, etc...)
But I think it might help in finding solutions sometimes to view it differently and say:
It is possible to declare an arbitrary type, built by assembling other types, for which the evaluation by the compiler can be delayed until it is effectively used.
Thus, it will be possible to inject the arguments of an IPack into another template...
Lastly, one should be able to detect if the operation succeeded with a testing trait making use of decltype and std::declval. (note: In the end, none of both have been used)
° Building Blocks
Step 1: IPack
template<typename...Ts>
struct IPack {
private:
template<typename U> struct Add1 {};
template<typename...Us> struct Add1<IPack<Us...>> { using Type = IPack<Us..., int>; };
public:
using Next = typename Add1<IPack<Ts...>>::Type;
static constexpr std::size_t Size = sizeof...(Ts);
};
using IPack0 = IPack<>;
using IPack1 = typename IPack0::Next;
using IPack2 = typename IPack1::Next;
using IPack3 = typename IPack2::Next;
constexpr std::size_t tp3Size = IPack3::Size; // 3
Now, one has a means to increase the number of arguments,
with a convenient way to retrieve the size of the IPack.
Next, one needs something to build an arbitrary type
by injecting the arguments of the IPack into another template.
Step 2: IPackInjector
An example on how the arguments of a template can be injected into another template.
It uses a template specialization to extract the arguments of an IPack,
and then, inject them into the Target.
template<typename P, template <typename...> class Target>
struct IPackInjector { using Type = void; };
template<typename...Ts, template <typename...> class Target>
struct IPackInjector<IPack<Ts...>, Target> { using Type = Target<Ts...>; };
template<typename T, typename U>
struct Victim;
template<typename P, template <typename...> class Target>
using IPInj = IPackInjector<P, Target>;
//using V1 = typename IPInj<IPack1, Victim>::Type; // error: "Too few arguments"
using V2 = typename IPInj<IPack2, Victim>::Type; // Victim<int, int>
//using V3 = typename IPInj<IPack3, Victim>::Type; // error: "Too many arguments"
Now, one has a means to inject the arguments of an IPack
into a Victim template, but, as one can see, evaluating Type
directly generates an error if the number of arguments does not
match the declaration of the Victim template...
Note
Have you noticed that the Victim template is not fully defined ?
It is not a complete type. It's only a forward declaration of a template.
The template to be tested will not need to be a complete type
for this solution to work as expected... ;)
If one wants to be able to pass this arbitrary built type to some detection trait one will have to find a way to delay its evaluation.
It turns out that the 'trick' (if one could say) is rather simple.
It is related to dependant names. You know this annoying rule
that enforces you to add ::template everytime you access a member template
of a template... In fact, this rule also enforces the compiler not to
evaluate an expression containing dependant names until it is
effectively used...
Oh I see ! ...
So, one only needs to prepare the IPackInjectors without
accessing its Type member, and then, pass it to our test trait, right ?
It could be done using something like that:
using TPI1 = IPackInjector<IPack1, Victim>; // No error
using TPI2 = IPackInjector<IPack2, Victim>; // No error
using TPI3 = IPackInjector<IPack3, Victim>; // No error
Indeed, the above example does not generate errors, and it confirms
that there is a means to prepare the types to be built and evaluate
them at later time.
Unfortunately, it won't be possible to pass these pre-configured
type builders to our test trait because one wants to use SFINAE
to detect if the arbitrary type can be instantiated or not.
And this is, once again, related to dependent name...
The SFINAE rule can be exploited to make the compiler silently
select another template (or overload) only if the substitution
of a parameter in a template is a dependant name.
In clear: Only for a parameter of the current template instantiation.
Hence, for the detection to work properly without generating
errors, the arbitrary type used for the test will have to be
built within the test trait with, at least, one of its parameters.
The result of the test will be assigned to the Success member...
Step 3: TypeTestor
template<typename T, template <typename...> class C>
struct TypeTestor {};
template<typename...Ts, template <typename...> class C>
struct TypeTestor<IPack<Ts...>, C>
{
private:
template<template <typename...> class D, typename V = D<Ts...>>
static constexpr bool Test(int) { return true; }
template<template <typename...> class D>
static constexpr bool Test(...) { return false; }
public:
static constexpr bool Success = Test<C>(42);
};
Now, and finally, one needs a machinery that will successively try
to instantiate our Victim template with an increasing number of arguments. There are a few things to pay attention to:
A template cannot be declared with no parameters, but it can:
Have only a parameter pack, or,
Have all its parameters defaulted.
If the test procedure begins by a failure, it means that the template must take more arguments. So, the testing must continue until a success, and then, continue until the first failure.
I first thought that it might make the iteration algorithm using template specializations a bit complicated... But after having thought a little about it, it turns out that the start conditions are not relevant.
One only needs to detect when the last test was true and next test will be false.
There must have a limit to the number of tests.
Indeed, a template can have a parameter pack, and such a template can receive an undetermined number of arguments...
Step 4: TemplateArity
template<template <typename...> class C, std::size_t Limit = 32>
struct TemplateArity
{
private:
template<typename P> using TST = TypeTestor<P, C>;
template<std::size_t I, typename P, bool Last, bool Next>
struct CheckNext {
using PN = typename P::Next;
static constexpr std::size_t Count = CheckNext<I - 1, PN, TST<P>::Success, TST<PN>::Success>::Count;
};
template<typename P, bool Last, bool Next>
struct CheckNext<0, P, Last, Next> { static constexpr std::size_t Count = Limit; };
template<std::size_t I, typename P>
struct CheckNext<I, P, true, false> { static constexpr std::size_t Count = (P::Size - 1); };
public:
static constexpr std::size_t Max = Limit;
static constexpr std::size_t Value = CheckNext<Max, IPack<>, false, false>::Count;
};
template<typename T = int, typename U = short, typename V = long>
struct Defaulted;
template<typename T, typename...Ts>
struct ParamPack;
constexpr std::size_t size1 = TemplateArity<Victim>::Value; // 2
constexpr std::size_t size2 = TemplateArity<Defaulted>::Value; // 3
constexpr std::size_t size3 = TemplateArity<ParamPack>::Value; // 32 -> TemplateArity<ParamPack>::Max;
° Conclusion
In the end, the algorithm to solve the problem is not that much complicated...
After having found the 'tools' with which it would be possible to do it, it only was a matter, as very often, of putting the right pieces at the right places... :P
Enjoy !
° Important Footnote
Here is the reason why the word intantiate(d) has been stricken at the places where it was used in relation to the Victim template.
The word instantiate(d) is simply not the right word...
It would have been better to use try to declare, or to alias the type of a future instantiation of the Victim template.
(which would have been extremely boring) :P
Indeed, none of the Victim templates gets ever instantiated within the code of this solution...
As a proof, it should be enough to see that all tests, made in the code above, are made only on forward declarations of templates.
And if you're still in doubt...
using A = Victim<int>; // error: "Too few arguments"
using B = Victim<int, int>; // No errors
template struct Victim<int, int>;
// ^^^^^^^^^^^^^^^^
// Warning: "Explicit instantiation has no definition"
In the end, there's a full sentence of the introduction which might be stricken, because this solution seems to demonstrate that:
It is possible to 'count' the number of parameters declared by a template...
Without instantiation of this template.
#include <utility>
#include <iostream>
template<template<class...>class>
struct ztag_t {};
template <template<class>class T>
constexpr std::size_t template_size_helper(ztag_t<T>) {
return 1;
}
template <template<class, class>class T>
constexpr std::size_t template_size_helper(ztag_t<T>) {
return 2;
}
template <typename T>
class MyTemplateClass { };
template <typename T, typename U>
class MyTemplateClass2 { };
template<template<class...>class T>
struct template_size:
std::integral_constant<
std::size_t,
template_size_helper(ztag_t<T>{})
>
{};
int main() {
std::cout << template_size<MyTemplateClass>::value << "\n";
std::cout << template_size<MyTemplateClass2>::value << "\n";
}
I know of no way without writing out the N overloads to support up to N arguments.
Live example.
Reflection will, of course, make this trivial.
° Before Reading This Post
This post does not answer to "How to get the number of parameters",
it answers to "how to get the number of arguments"...
It is let here for two reasons:
It might help someone who would have mixed up (like I did)
the meaning of parameters and arguments.
The techniques used in this post are closely related to the ones used
to produce the correct answer I've posted as a separate answer.
See my other answer for finding "the number of parameters" of a template.
The answer of Elliott looks more like what one usually does (though the primary template should be fully defined and "do something" IMHO). It uses a template specialization for when a template is passed as argument to the primary template.
Meanwhile, the answer of Elliott vanished...
So I've posted something similar to what he showed below.
(see "Generic Solution")
But, just to show you that you weren't that far from a working solution, and, because I noticed that you have used a function for your try, and, you declared this function constexpr, you could have written it like that:
Note
It is a 'fancy solution', but it works...
template <typename T> class MyTemplateClass {};
template <typename T, typename U> class MyTemplateClass2 {};
template <template <typename...> class T, typename...Ts>
constexpr const size_t template_size(T<Ts...> && v)
{
return sizeof...(Ts);
}
// If the target templates are complete and default constructible.
constexpr size_t size1 = template_size(MyTemplateClass<int>{});
constexpr size_t size2 = template_size(MyTemplateClass2<int, short>{});
// If the target templates are complete but NOT default constructible.
constexpr size_t size3 = template_size(decltype(std::declval<MyTemplateClass<int>>()){});
constexpr size_t size4 = template_size(decltype(std::declval<MyTemplateClass2<int, short>>()){});
Explanation
You said "because Ts can not be referenced", which is true and false, because of the way you made the declaration of template_size.
That is, a template template parameter cannot declare parameters itself (where you placed Ts in the declaration of the function template). It is allowed to do so to give a clue of what the template argument is expected to receive as argument, but it is not a declaration of a parameter name for the current template declaration...
(I hope it's clear enough) ;)
Obviously, it might be a little bit over complicated, but it worth knowing I think that such a construct is possible also... ;)
Generic Solution
template <typename T> class MyTemplateClass {};
template <typename T, typename U> class MyTemplateClass2 {};
template<typename T>
struct NbParams { static constexpr std::size_t Value = 0; };
template<template <typename...> class C, typename...Ts>
struct NbParams<C<Ts...>> { static constexpr std::size_t Value = sizeof...(Ts); };
constexpr size_t size1 = NbParams<MyTemplateClass<int>>::Value;
constexpr size_t size2 = NbParams<MyTemplateClass2<int, short>>::Value;
That is the regular way one does this kind of things... ;)

Is there a way to use variable template as a parameter?

I want to declare a function, which will take as a parameter a variable (let's say, int), which should be parametrized by a class. Speaking in terms of lambda calculus, I want my parameter to have a kind * -> int.
Example of a function I want to be able to write (Spec is the variable):
template <??? Specification, typename T>
auto make_array() {
return std::array<T, Specification<T>>;
}
Since C++14 we have variable templates, so we can do something like this:
template <typename T>
constexpr int digits = std::numeric_limits<T>::digits;
The problem is, how do I pass that into a function? In the notes section of cppreference it is stated that
Variable templates cannot be used as template template arguments.
But does that mean that there is actually no way to pass parametrized variable as a function parameter? What you can do is, for example, create a class which has a static field denoting value, but an obvious drawback is that the users of my function must derive from that class.
I believe there might be some workaround using SFINAE, but I lack skills in that area.
Unless you insist on using a variable template, you can use a type trait:
template <typename T> struct Specification;
you can specialize it for example for int:
template <>
struct Specification<int> {
static constexpr size_t value = 42;
};
and as you want to have different Specifications, pass it as template template parameter:
template <template<class> class S, typename T>
auto make_array() {
return std::array<T, S<T>::value>{};
}
Complete example:
#include <array>
#include <cstddef>
template <template<class> class S, typename T>
auto make_array() {
return std::array<T, S<T>::value>{};
}
template <typename T> struct Specification;
template <>
struct Specification<int> {
static constexpr size_t value = 42;
};
int main(){
auto x = make_array<Specification,int>();
}
Note that I was rather verbose for the sake of clarity. You can save a bit of typing by using std::integral_constant, eg:
template <>
struct Specification<double> : std::integral_constant<size_t,3> {};
As an follow-up on idclev's answer, you can avoid the need to explicitly specialise for the different types for individual "specifications" just by inheriting from e.g. integral_constant. For example, your desired usage was something like
template <template <typename T> int Specification, typename T>
auto make_array() {
return std::array<T, Specification<T>>;
}
template <typename T>
constexpr int digits = std::numeric_limits<T>::digits;
// ...
auto foo = make_array<digits, double>();
However, as you have noted, this is impossible: you cannot pass variable templates as template-template parameters. However, by turning digits into a structure directly you can do this:
template <template <typename T> class Specification, typename T>
auto make_array() {
// we no longer have the guarantee of the type here, unfortunately
// but you can use a static_assert to improve error messages
// (also using `std::size_t` here for correctness)
static_assert(
std::is_convertible<decltype(Specification<T>::value), std::size_t>::value,
"value must be convertible to size_t");
return std::array<T, Specification<T>::value>;
}
// use a type rather than a variable template
// we just inherit from integral constant to save on some typing
// (though you could do this explicitly as well)
template <typename T>
struct digits : std::integral_constant<int, std::numeric_limits<T>::digits> {};
// ...
// same call!
auto foo = make_array<digits, double>();

Why give a typename template parameter a default value of 0 in C++?

The short version (Read if you have no patience like me):
What does setting a typename template parameter to a default of 0 do in C++?
My enable if struct:
/**
* #brief Can be used to enable a template definition using a boolean value
*/
template<lfBool Condition>
struct lfEnableIf
{ };
template<>
struct lfEnableIf<true>
{
typedef lfInt Type;
};
My boolen constant struct:
template<lfBool Val>
struct lfBoolConstant
{
static const lfBool Value = Val;
};
typedef lfBoolConstant<true> lfTrueType;
typedef lfBoolConstant<false> lfFalseType;
My type trait struct (just one of the specializations):
template <typename NumT> struct lfIsArithmetic : lfFalseType{};
template <> struct lfIsArithmetic<lfChar> : lfTrueType{};
And finally my usage of it all:
template<typename T, typename lfEnableIf<lfIsArithmetic<T>::Value>::Type = 0>
struct Test
{
static void print()
{
std::cout << "OK!" << std::endl;
}
};
int main()
{
Test<lfFloat>::print();
Test<lfBool>::print();
return 0;
}
Sorry for the poor for formatting I am writing this on my cell phone.
The long version (Please read if you have the patience so you understand why there is not much code):
So, I am on vacation and don't have access to my workstation or laptop so I thought I would try out AIDE, which if you are unaware is an IDE for android that can compile C++. At home I am designing a game engine that includes Boost and I figured I would try to create something similar to the enable_if structures that are in the Boost.Core library. I got it mostly working but it would not compile unless I set the enable if structure in the template I was enabling to default to 0! This is what you do with the Boost enable_if and disable_if templates. So what does setting a typename template parameter to a default of 0 do in C++?
Thanks!
This line
template<typename T, typename lfEnableIf<lfIsArithmetic<T>::Value>::Type = 0>
declares a named template type parameter T and a nameless template value parameter of type lfEnableIf<lfIsArithmetic<T>::Value>::Type, i.e. the second parameter declarations is basically a more convoluted version of simple
template <int N = 0> struct S {};
The type lfEnableIf<lfIsArithmetic<T>::Value>::Type resolves into type lfInt when the enabling condition is met, meaning that the whole thing in such cases is equivalent to
template<typename T, lfInt = 0>
However, since type of that second template parameter is a nested type of a dependent template lfEnableIf, you are required to use the keyword typename to tell the compiler that member Type actually refers to a type and not to something else (i.e. to disambiguate the situation).
Again, the second parameter of the template is nameless, but you can give it a name, if you wish. It won't change anything
template<typename T, typename lfEnableIf<lfIsArithmetic<T>::Value>::Type V = 0>
In the above example I called it V. The name of that parameter is not used anywhere in the template, which is why there's no real need to specify it explicitly. It is a dummy parameter, which is also the reason it has a dummy default value (you can replace 0 with 42 - it won't change anything either).
In this case keyword typename creates a misleading similarity between the two parameter declarations of your template. In reality in these parameter declarations the keyword typename serves two very very very different unrelated purposes.
In the fist template parameter declaration - typename T - it declares T as a template type parameter. In this role keyword typename can be replaced with keyword class
template <class T, ...
In the second declaration - typename lfEnableIf<lfIsArithmetic<T>::Value>::Type = 0 - it serves a secondary purpose - it just tells the compiler that lfEnableIf<lfIsArithmetic<T>::Value>::Type is a type and thus turns the whole thing into a value parameter declaration. In this role keyword typename cannot be replaced with keyword class.

C++ Template specialization with class as return type and enum as parameter

I don't have a lot of experience using templates but I'm trying to do template specialization based on enums with a function which returns different classes. Below is the sample code (or rather what I'm trying to accomplish):
class Foo {
// member variables
};
class Cat {
// member variables
};
enum MyType{ A, B, C};
// Header file
template<class T, MyType U> std_shared_ptr<T> process();
// cpp file / implementation
template<> std_shared_ptr<Foo> process<Foo, A>()
{
}
template<> std_shared_ptr<Cat> process<Cat, C>();
{
}
Can someone help me in figuring out what I'm missing here or doing wrong? I tried searching it and found some solutions for handling enum types (Template specialization for enum), however, can't figure out how to put it together with a template return type in a function.
EDIT:
What I'm trying do here is to do template specialization based on enum type as argument to a function. And the same function returns a template class as well. So the function has two templates here: T (return param) and U (input param which is an enum). Is it possible to do so?
EDIT:
Modified the above sample for the right behavior.
You cannot partially specialize template functions.
The value, not the type, of a function parameter cannot change the type of the return value. The value of a non-type template parameter can change the type of the return value, but that is passed within the <> and must be compile-time determined, not within the ()s.
Tags may help.
template<MyType X>
using my_type_tag_t=std::integral_constant<MyType, X>;
template<MyType X>
constexpr my_type_tag_t<X> my_type_tag = {};
template<class T>struct tag_t{using type=T;};
template<class Tag>using type=typename Tag::type;
template<MyType>
struct my_type_map;
template<>
struct my_type_map<MyType::A>:tag<Foo>{};
template<>
struct my_type_map<MyType::B>:tag<Cat>{};
then:
template<MyType X>
std::shared_ptr<type<my_type_map<X>>>
process( my_type_tag_t<X> );
where you call process( my_type_tag<A> ) to get a shared_ptr<Foo> out of it.
Implementations look like:
template<>
std::shared_ptr<Foo>
process( my_type_tag_t<MyType::A> ) {
// blah
}
still inelegant, and probably doesn't solve your problem, but it is close to your described solution.

template expression to select the first defined type

I need a template expression that selects the first argument type if the first argument is defined, else the second argument type.
select<int, float>::type // type evaluates to int
select<an_undefined_type, float>::type // type evaluates to float
... and the solution has to work with C++03 and Boost 1.33.1 :(
My goal is to accept both int and boost::optional<int> as a function template parameter T, so I can do something like:
template<typename T>
void fn(T& t)
{
int x = std::numeric_limits<select<T::value_type, T>::type>::digits;
}
since boost::optional<int>::value_type is defined.
C++11 solutions are also appreciated.
I don't see a way to do this with template specialization, since I'm trying to specialize not on types but on concepts. Basically, I would need one specialization that matches the any_integer concept and one specialization that matches the boost::optional<any_integer> concept.
I guess with C++11 I could accomplish this specific goal with:
std::conditional<std::is_integral<T>::value, T, T::value_type>::value
but I don't have C++11, and I want the more general solution.
I don't think you can achieve the exact notation you are looking for. However, I think you can use a slightly different notation to achieve what you are semantically after. The problem with your current notation
int x = std::numeric_limits<select<T::value_type, T>::type>::digits;
is that select<T0, T1> expects two types, i.e., the requirement for the types to be present isn't on the select<T0, T1> template but on the function calling it. The way I would change this is to use
int x = std::numeric_limits<select<typename get_value_type<T>::type, T>::type>::digits;
Now all what needs to happen is to have a get_value_type<T> which yields the nested type if present and some type select<T0, T1> is going to ignore if arrives there, e.g., void (or a custom marker type). The get_value_type<T> template should be fairly simple (I saw Dirk Holsopple's answer but I couldn't get it work):
template <typename T>
struct has_value_type
{
typedef char (&true_type)[1];
typedef char (&false_type)[2];
template <typename D> static true_type test(typename D::value_type*);
template <typename D> static false_type test(...);
enum { value = sizeof(test<T>(0)) == 1 };
};
template <typename T, bool = has_value_type<T>::value >
struct get_value_type
{
typedef T type; // EDIT
};
template <typename T>
struct get_value_type<T, true>
{
typedef typename T::value_type type;
};
Obviously, you might want to define your type-traits slightly different so you can use something like
int x = std::numeric_limits<typename get_type<T>::type>::digits;
This would return the nested type if there is a value_type and a type T otherwise.