I was working with a templated class which takes a set of integers. The code was like,
template
struct work{ ... };
I'm not entirely sure what you are asking about. I thought the sample you gave was interesting and played a little bit with it.
I came up with an implementation that makes span<a,b>
to be a template alias for indexes<a, ..., b>
, using the trick of type deduction in constexpr
functions:
template <int a, int b> using span = decltype(expand_span<a,b>());
Now you can have your cake and eat it:
////////////////////////////////////////////////////////////////
// using indirect template arguments
template<typename> struct indirect_work { };
void test_indirect()
{
indirect_work<indexes<1,2,3,4>> x;
indirect_work<span<1,4>> y;
x = y; // x and y are of identical types
static_assert(std::is_same<indexes<1,2,3,4>, span<1,4>>::value, "fact check");
}
But, perhaps more interestingly, you can still have your primary work
template take a raw <int...>
template argument list:
////////////////////////////////////////////////////////////////
// using direct template arguments
template<int...> struct direct_work { };
// deduction alias:
template<int... direct> constexpr direct_work<direct...> deduction_helper(indexes<direct...>);
template <typename Idx> using deduce = decltype(deduction_helper(Idx{}));
void test_direct()
{
direct_work<1,2,3,4> x;
deduce<indexes<1,2,3,4>> y;
deduce<span<1,4>> z;
static_assert(std::is_same<decltype(x), decltype(y)>::value, "fact check");
static_assert(std::is_same<decltype(x), decltype(z)>::value, "fact check");
}
See a complete working demonstration here: gcc on ideone. I compiled it with clang locally.
Code for expand_span
duplicated here in case link should go dead:
#include <type_traits>
template <int...> struct indexes {};
namespace {
template<int a, int... other>
constexpr indexes<a, other...> combine(indexes<other...> deduce);
template<int a, int b, typename Enable = void> struct expand_span_; // primary
template<int a, int b>
struct expand_span_<a, b, typename std::enable_if< (a==b), void >::type> {
static constexpr indexes<a> dispatch();
};
template<int a, int b>
struct expand_span_<a, b, typename std::enable_if< (a<b), void >::type> {
static constexpr decltype(combine<a>(expand_span_<a+1, b>::dispatch()))
dispatch();
};
template<int a, int b>
constexpr auto expand_span() -> decltype(expand_span_<a,b>::dispatch());
}
template <int a, int b> using span = decltype(expand_span<a,b>());
////////////////////////////////////////////////////////////////
// using indirect template arguments
template<typename> struct indirect_work { };
void test_indirect()
{
indirect_work<indexes<1,2,3,4>> x;
indirect_work<span<1,4>> y;
x = y; // x and y are of identical types
static_assert(std::is_same<indexes<1,2,3,4>, span<1,4>>::value, "fact check");
}
////////////////////////////////////////////////////////////////
// using direct template arguments
template<int...> struct direct_work { };
// deduction alias:
template<int... direct> constexpr direct_work<direct...> deduction_helper(indexes<direct...>);
template <typename Idx> using deduce = decltype(deduction_helper(Idx{}));
void test_direct()
{
direct_work<1,2,3,4> x;
deduce<indexes<1,2,3,4>> y;
deduce<span<1,4>> z;
static_assert(std::is_same<decltype(x), decltype(y)>::value, "fact check");
static_assert(std::is_same<decltype(x), decltype(z)>::value, "fact check");
}
int main()
{
test_indirect();
test_direct();
}
Compound types can be constructed using the declarator syntax - found in [dcl.decl]
.
Underlying this syntax are six fundamental constructs, within which any T
can be substituted by any of the other constructs in the list. [In the following, (T)
represents a list of zero or more types (which may end in '...'), and <T>
represents a list of one or more types.]
T // T
T* // pointer to T
T& // reference to T
T[n] // array of size 'n' of T
T C::* // pointer to C::member of type T
T (T) // function taking '(T)' and returning T
EDIT: The type of a class template specialization can be subsituted for any T
:
C<T> // specialization of class template C with arguments '<T>'
There are combinations of the above that produce constructs which have special significance:
T (*)(T) // pointer to function taking '(T)' and returning T
T (C::*)(T) // pointer to C::member-function taking '(T)' and returning T
Additionally, some of the constructs may be cv-qualified:
const T // const T
T* const // const pointer to T
T C::* const // const pointer to C::member of type T
Not all of the combinations result in valid types. According to [basic.compound]
, only the following combinations can be used:
Compound types can be constructed in the following ways:
- arrays of objects of a given type
- functions, which have parameters of given types and return void or references or objects of a given type
- pointers to void or objects or functions (including static members of classes) of a given type
- references to objects or functions of a given type
- pointers to non-static class members, which identify members of a given type within objects of a given class
Additional restrictions are mentioned:
[dcl.ptr]
there are no pointers to references
[dcl.ref]
There shall be no references to references, no arrays of references, and no pointers to references
[dcl.mptr]
A pointer to member shall not point to ... a member with reference type, or "cv void."
[dcl.fct]
The parameter list (void) is equivalent to the empty parameter list. Except for this special case, void shall not be a parameter type. ... 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.
Some of the possible constructs cannot be used as template-arguments. When you explicitly specify a set of template-arguments, the compiler must check that the template-arguments can be substituted for the template-parameters without resulting in an 'invalid type'. According to [temp.deduct]\2
, the following constructs constitute invalid types:
Type deduction may fail for the following reasons:
Attempting to create an array with an element type that is void, a function type, or a reference type, or attempting to create an array with a size that is zero or negative.
template <class T> int f(T[5]); int I = f<int>(0); int j = f<void>(0); // invalid array
Attempting to use a type that is not a class type in a qualified name.
template <class T> int f(typename T::B*); int i = f<int>(0);
Attempting to use a type in the qualifier portion of a qualified name that names a type when that type does not contain the specified member, or if the specified member is not a type where a type is required.
template <class T> int f(typename T::B*); struct A {}; struct C { int B; }; int i = f<A>(0); int j = f<C>(0);
Attempting to create a pointer to reference type.
Attempting to create a reference to a reference type or a reference to void.
Attempting to create "pointer to member of T" when T is not a class type.
template <class T> int f(int T::*); int i = f<int>(0);
Attempting to perform an invalid conversion in either a template argument expression, or an expression used in the function declaration.
template <class T, T*> int f(int); int i2 = f<int,1>(0); // can’t conv 1 to int*
Attempting to create a function type in which a parameter has a type of void.
- Attempting to create a cv-qualified function type.
EDIT: This is based on C++03, but also applies to C++11 (which adds rvalue reference types)