template parameter default value with dependancy to deduced template parameters - c++

I am aware this question is quite similar to This post, however the cited post was about class template argument deduction. Here my question is about function template argument deduction.
I currently have the following piece of code, which enables users to specify the algorithm a method should use in order to compute some values.
(Using a strategy Pattern is not applicable in this case for various reasons which are not relevant here)
#include <iostream>
#include <typeinfo>
template<typename T>
class BasicStrategy
{
public:
BasicStrategy(T& i_Input)
{
std::cout << "Applying Basic Strategy to type : " << typeid(T).name() << " With value : " << i_Input <<std::endl;
}
};
template<typename T>
class ComplexStrategy
{
public:
ComplexStrategy(T& i_Input)
{
std::cout << "Applying Complex Strategy to type : " << typeid(T).name() << " With value : " << i_Input <<std::endl;
}
};
template<typename T, typename STRATEGY = BasicStrategy<T>>
void Func(T& i_Input)
{
STRATEGY MyStrategy(i_Input);
}
int main()
{
int i = 12;
double d = 24;
Func(i);
Func(d);
Func<int, ComplexStrategy<int>>(i);
Func<double, ComplexStrategy<double>>(d);
return 0;
}
I would like to know if it would be possible to simplify the interface of "Func()" in order to exempt the user from specifying redundant types if not using the "BasicStrategy"
For instance, the "ideal" interface would look like this (i realize this is not possible though):
int main()
{
int i = 12;
double d = 24;
Func(i);
Func(d);
Func<ComplexStrategy>(i);
Func<ComplexStrategy>(d);
return 0;
}
Of course, i could declare Func() like this
template<typename STRATEGY, typename T>
void Func(T& i_Input)
Which would not require the user to specify the type T twice, however, this prevents me from assigning a default strategy to the method, which would break a lot of existing code and making it less readable overall.
Is there a clean solution to this problem or is this a choice i have to make between the two options?

You can use a template template parameter to achieve exactly what you want:
template<template <typename> typename Strategy = BasicStrategy, typename T>
void Func(T& i_Input)
{
Strategy<T> MyStrategy(i_Input);
}
That way what you mentioned as ideal would work: https://godbolt.org/z/aosPzqa3r

i realize this is not possible though
It is when you make STRATEGY a template template argument.
#include <iostream>
#include <typeinfo>
template<typename T>
class BasicStrategy
{
public:
BasicStrategy(T& i_Input)
{
std::cout << "Applying Basic Strategy to type : " << typeid(T).name() << " With value : " << i_Input <<std::endl;
}
};
template<typename T>
class ComplexStrategy
{
public:
ComplexStrategy(T& i_Input)
{
std::cout << "Applying Complex Strategy to type : " << typeid(T).name() << " With value : " << i_Input <<std::endl;
}
};
template <template<typename> typename STRATEGY = BasicStrategy, typename T>
void Func(T& i_Input)
{
STRATEGY<T> MyStrategy(i_Input);
}
int main()
{
int i = 12;
double d = 24;
Func(i);
Func(d);
Func<ComplexStrategy>(i);
Func<ComplexStrategy>(d);
return 0;
}
Live Demo

You may switch to use functor separating Strategy from T:
template<template<typename T> typename STRATEGY = BasicStrategy>
struct t_Func
{
template<typename T>
void operator ()(T& i_Input) const
{
STRATEGY<T> MyStrategy(i_Input);
}
};
template<template<typename T> typename STRATEGY = BasicStrategy>
constexpr t_Func<STRATEGY> Func{};
int main()
{
int i = 12;
double d = 24;
Func<>(i);
Func<>(d);
Func<ComplexStrategy>(i);
Func<ComplexStrategy>(d);
return 0;
}
online compiler

C++20 Answer:
Using a template template parameter for the strategy and auto for the function parameter allows you to get the syntax you want. Doing so gives you
template<template<typename> typename STRATEGY = BasicStrategy>
void Func(auto& i_Input)
{
STRATEGY MyStrategy(i_Input);
}
Using your example of
int main()
{
int i = 12;
double d = 24;
Func(i);
Func(d);
Func<ComplexStrategy>(i);
Func<ComplexStrategy>(d);
return 0;
}
it outputs
Applying Basic Strategy to type : i With value : 12
Applying Basic Strategy to type : d With value : 24
Applying Complex Strategy to type : i With value : 12
Applying Complex Strategy to type : d With value : 24

Related

Template function deduction fail on std::conditional argument

Please, before marking this as a duplicate of This question read the entirety of the post
This piece of code fails to compile, with a template deduction error:
#include <iostream>
#include <type_traits>
template<typename T = float, int N>
class MyClass
{
public:
template<typename DATA_TYPE>
using MyType = std::conditional_t<(N>0), DATA_TYPE, double>;
MyType<T> Var;
void Foo()
{
Bar(Var);
}
template<typename TYPE>
void Bar(MyType<TYPE> Input)
{
std::cout << typeid(Input).name() << std::endl;
}
};
int main()
{
MyClass<float, 1> c;
c.Foo();
return 0;
}
I understand the point that was made in the question i linked above, which is that "the condition which allows to choose the type to be deduced depends on the type itself", however, why would the compiler fail in the specific case i provided as the condition here seems to be fully independent from the type, or is there something i'm missing?
I would be more than happy if someone could refer to a section of the c++ standard that would allow me to fully understand this behaviour.
As the linked question, TYPE is non deducible. MyType<TYPE> is actually XXX<TYPE>::type.
You have several alternatives, from your code, I would say one of
Bar no longer template:
template<typename T = float, int N>
class MyClass
{
public:
template<typename DATA_TYPE>
using MyType = std::conditional_t<(N>0), DATA_TYPE, double>;
MyType<T> Var;
void Foo()
{
Bar(Var);
}
void Bar(MyType<T> Input)
{
std::cout << typeid(Input).name() << std::endl;
}
};
requires (or SFINAE/specialization for pre-c++20):
template<typename T = float, int N>
class MyClass
{
public:
template<typename DATA_TYPE>
using MyType = std::conditional_t<(N>0), DATA_TYPE, double>;
MyType<T> Var;
void Foo()
{
Bar(Var);
}
template<typename TYPE>
void Bar(TYPE Input) requires(N > 0)
{
std::cout << typeid(Input).name() << std::endl;
}
void Bar(double Input) requires(N <= 0)
{
std::cout << typeid(Input).name() << std::endl;
}
};

passing template for later use in other struct/class context

I have some classes which need to define a template which can be used in generic code parts as type later.
In real world code the forwarded templates have a lot more parameters and it is not really nice to read the code.
Q: Is it possible to define the template in some syntax instead of writing it as alias template as given in the following example? I simple would avoid repeating of all the template parameters two times of each alias declaration.
The real world template also have some non type template parameters so simply using <PARMS...> will not work.
Example:
#include <iostream>
template < typename T>
struct A
{
static void Do(T t) { std::cout << "A " << t << std::endl;}
};
template < typename T>
struct B
{
static void Do(T t) { std::cout << "B " << t << std::endl;}
};
struct UseA
{
// using the alias template works as expected, but...
template < typename T>
using USE = A<T>;
// is there any chance to write something like:
// using USE = A;
// to simply avoid replication of template parameters?
};
struct UseB
{
template < typename T>
using USE = B<T>;
};
int main()
{
UseA::USE<int>::Do(1);
UseB::USE<std::string>::Do("Hallo");
}
What you are asking cannot be done. You always have to define the whole type list. The reason is, that one could have default overloads for the same type. For example, in the following A<int, 3>, A<int> and A<> are all valid. The compiler does not know which one you want:
template <class T, int Value = 42>
struct A {};
auto test() {
auto a = A<int, 3>{};
auto b = A<int>{};
auto c = A<>{};
}
If you don't want to write the type lists, I would recommend you to switch to templatizing more of your classes, so they don't need to know about the implementation details. Like:
#include <iostream>
template < typename T>
struct A
{
static void Do(T t) { std::cout << "A " << t << std::endl;}
};
template < typename T>
struct B
{
static void Do(T t) { std::cout << "B " << t << std::endl;}
};
template < typename T>
struct Use
{
using USE = T;
};
int main()
{
Use<A<int>>::USE::Do(1);
Use<B<std::string>>::USE::Do("Hallo");
}
Or alternatively, use containers for your non template type values:
#include <iostream>
template < int Value >
struct INT
{
static constexpr int value = Value;
};
template < bool Value >
struct BOOL
{
static constexpr bool value = Value;
};
template < typename T, typename Value >
struct A
{
static void Do(T t) { std::cout << "A " << t << Value::value << std::endl;}
};
template < typename T, typename Value>
struct B
{
static void Do(T t) { if (Value::value) std::cout << "B " << t << std::endl;}
};
template <template<typename...> class T, typename ...Param>
using USE = T<Param...>;
int main()
{
USE<A, int, INT<42>>::Do(1);
USE<B, std::string, BOOL<true>>::Do("Hallo");
}

Asking at runtime if alternative in variant satisfies a certain concept

I have the following code where I try to detect what concept(member functions in this case) alternative in variant holds.
It is verbose and relatively ugly.
Is there a nicer way to do this?
Note that I do not want to use inheritance, and I do not want to use static polymorphism(let's assume what engine is used is not known at compile time).
In simple terms I am looking for runtime mix of if constexpr(that only checks concepts at compile time) and std::holds_alternative (that can only check for if specific type is in variant, not if any of the types satisfying the concept is in variant).
#include <iostream>
#include <variant>
struct simple_engine1{
};
struct simple_engine2{
};
struct complex_engine1{
void reduce_thrust(int perc){
std::cout<<"reducing thrust " << perc << "% " << std::endl;
}
};
struct complex_engine2{
void reduce_thrust(int perc){
std::cout<<"reducing thrust " << perc << "% " << std::endl;
}
};
template< class, class = std::void_t<> >
struct has_reduce_thrust : std::false_type { };
template< class T >
struct has_reduce_thrust<T,
std::void_t<decltype( std::declval<T>().reduce_thrust(42) )>
> : std::true_type { };
static_assert(!has_reduce_thrust<simple_engine1>::value);
static_assert(!has_reduce_thrust<simple_engine1>::value);
static_assert(has_reduce_thrust<complex_engine1>::value);
struct visitor{
template<typename T>
void operator()(T& t){
dispatch(t, has_reduce_thrust<T>{});
}
template<typename T>
void dispatch(T& t, std::true_type /*has_reduce_thrust*/){
t.reduce_thrust(perc);
reduced_thrust=true;
}
template<typename T>
void dispatch(T& , std::false_type){
reduced_thrust=false;
}
int perc = 0;
bool reduced_thrust = false;
};
// tries to reduce speed by reducing thrust if engine supports it, if not
// it fires reverse engines(more expensive wrt fuel usage)
void reduce_speed(std::variant<simple_engine1, simple_engine2, complex_engine1, complex_engine2>* var_engine){
visitor v;
v.perc = 47;
std::visit(v, *var_engine);
if (v.reduced_thrust) {
std::cout << "reduced thrust\n";
} else {
std::cout << "activating reverse engines\n";
}
}
int main() {
std::variant<simple_engine1, simple_engine2, complex_engine1, complex_engine2> var_engine{simple_engine1{}};
reduce_speed(&var_engine);
var_engine = complex_engine2{};
reduce_speed(&var_engine);
var_engine = simple_engine2{};
reduce_speed(&var_engine);
var_engine = complex_engine2{};
reduce_speed(&var_engine);
}
You can simplify the visitor a lot by using if constexpr:
struct visitor{
template<typename T>
void operator()(T& t) {
if constexpr (has_reduce_thrust<T>::value) {
t.reduce_thrust(perc);
reduced_thrust = true;
}
else {
reduced_thrust = false;
}
}
int perc = 0;
bool reduced_thrust = false;
};
You could then abstract further by accepting any predicate and two functions for either branch of the if constexpr:
template <template <class, class... /*SFINAE friendly*/> class TypePred,
class MatchedFunc, class UnmatchedFunc>
class predicated_visitor {
public:
predicated_visitor(MatchedFunc matchedFunc, UnmatchedFunc unmatchedFunc)
: _matchedFunc(matchedFunc), _unmatchedFunc(unmatchedFunc) {}
template <typename T>
void operator()(T& t) {
if constexpr (TypePred<T>::value)
_matchedFunc(t);
else
_unmatchedFunc(t);
}
private:
MatchedFunc _matchedFunc;
UnmatchedFunc _unmatchedFunc;
};
template <template <class, class... /*SFINAE friendly*/> class TypePred,
class F1, class F2>
auto makePredicatedVisitor(F1 f1, F2 f2) {
return predicated_visitor<TypePred, F1, F2>(f1, f2);
}
The resulting code is quite nice I feel:
void reduce_speed(std::variant<simple_engine1, simple_engine2, complex_engine1,
complex_engine2>* var_engine) {
int perc = 47;
bool reducedThrust = false;
auto reduceableThrustAction = [perc, &reducedThrust](auto& t) {
t.reduce_thrust(perc);
reducedThrust = true;
};
auto alternativeAction = [](auto& t) {
}; // Could explicitly set reduceThrust to false for clarity.
auto thrust_visitor = makePredicatedVisitor<has_reduce_thrust>(
reduceableThrustAction, alternativeAction);
std::visit(thrust_visitor, *var_engine);
if (reducedThrust) {
std::cout << "reduced thrust\n";
} else {
std::cout << "activating reverse engines\n";
}
}
Demo
This example compiles to essentially the same assembly code as yours but can be reused in any way you like.
I apologize for the inconsistent capitalization...

Could you please explain below code ? It compiles fine. Its related to check whether given class is base of another class [duplicate]

I want to get into more template meta-programming. I know that SFINAE stands for "substitution failure is not an error." But can someone show me a good use for SFINAE?
I like using SFINAE to check boolean conditions.
template<int I> void div(char(*)[I % 2 == 0] = 0) {
/* this is taken when I is even */
}
template<int I> void div(char(*)[I % 2 == 1] = 0) {
/* this is taken when I is odd */
}
It can be quite useful. For example, i used it to check whether an initializer list collected using operator comma is no longer than a fixed size
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i, char(*)[M <= N] = 0) { /* ... */ }
}
The list is only accepted when M is smaller than N, which means that the initializer list has not too many elements.
The syntax char(*)[C] means: Pointer to an array with element type char and size C. If C is false (0 here), then we get the invalid type char(*)[0], pointer to a zero sized array: SFINAE makes it so that the template will be ignored then.
Expressed with boost::enable_if, that looks like this
template<int N>
struct Vector {
template<int M>
Vector(MyInitList<M> const& i,
typename enable_if_c<(M <= N)>::type* = 0) { /* ... */ }
}
In practice, i often find the ability to check conditions a useful ability.
Heres one example (from here):
template<typename T>
class IsClassT {
private:
typedef char One;
typedef struct { char a[2]; } Two;
template<typename C> static One test(int C::*);
// Will be chosen if T is anything except a class.
template<typename C> static Two test(...);
public:
enum { Yes = sizeof(IsClassT<T>::test<T>(0)) == 1 };
enum { No = !Yes };
};
When IsClassT<int>::Yes is evaluated, 0 cannot be converted to int int::* because int is not a class, so it can't have a member pointer. If SFINAE didn't exist, then you would get a compiler error, something like '0 cannot be converted to member pointer for non-class type int'. Instead, it just uses the ... form which returns Two, and thus evaluates to false, int is not a class type.
In C++11 SFINAE tests have become much prettier. Here are a few examples of common uses:
Pick a function overload depending on traits
template<typename T>
std::enable_if_t<std::is_integral<T>::value> f(T t){
//integral version
}
template<typename T>
std::enable_if_t<std::is_floating_point<T>::value> f(T t){
//floating point version
}
Using a so called type sink idiom you can do pretty arbitrary tests on a type like checking if it has a member and if that member is of a certain type
//this goes in some header so you can use it everywhere
template<typename T>
struct TypeSink{
using Type = void;
};
template<typename T>
using TypeSinkT = typename TypeSink<T>::Type;
//use case
template<typename T, typename=void>
struct HasBarOfTypeInt : std::false_type{};
template<typename T>
struct HasBarOfTypeInt<T, TypeSinkT<decltype(std::declval<T&>().*(&T::bar))>> :
std::is_same<typename std::decay<decltype(std::declval<T&>().*(&T::bar))>::type,int>{};
struct S{
int bar;
};
struct K{
};
template<typename T, typename = TypeSinkT<decltype(&T::bar)>>
void print(T){
std::cout << "has bar" << std::endl;
}
void print(...){
std::cout << "no bar" << std::endl;
}
int main(){
print(S{});
print(K{});
std::cout << "bar is int: " << HasBarOfTypeInt<S>::value << std::endl;
}
Here is a live example: http://ideone.com/dHhyHE
I also recently wrote a whole section on SFINAE and tag dispatch in my blog (shameless plug but relevant) http://metaporky.blogspot.de/2014/08/part-7-static-dispatch-function.html
Note as of C++14 there is a std::void_t which is essentially the same as my TypeSink here.
Boost's enable_if library offers a nice clean interface for using SFINAE. One of my favorite usage examples is in the Boost.Iterator library. SFINAE is used to enable iterator type conversions.
Here's another (late) SFINAE example, based on Greg Rogers's answer:
template<typename T>
class IsClassT {
template<typename C> static bool test(int C::*) {return true;}
template<typename C> static bool test(...) {return false;}
public:
static bool value;
};
template<typename T>
bool IsClassT<T>::value=IsClassT<T>::test<T>(0);
In this way, you can check the value's value to see whether T is a class or not:
int main(void) {
std::cout << IsClassT<std::string>::value << std::endl; // true
std::cout << IsClassT<int>::value << std::endl; // false
return 0;
}
Examples provided by other answers seems to me more complicated than needed.
Here is the slightly easier to understand example from cppreference :
#include <iostream>
// this overload is always in the set of overloads
// ellipsis parameter has the lowest ranking for overload resolution
void test(...)
{
std::cout << "Catch-all overload called\n";
}
// this overload is added to the set of overloads if
// C is a reference-to-class type and F is a pointer to member function of C
template <class C, class F>
auto test(C c, F f) -> decltype((void)(c.*f)(), void())
{
std::cout << "Reference overload called\n";
}
// this overload is added to the set of overloads if
// C is a pointer-to-class type and F is a pointer to member function of C
template <class C, class F>
auto test(C c, F f) -> decltype((void)((c->*f)()), void())
{
std::cout << "Pointer overload called\n";
}
struct X { void f() {} };
int main(){
X x;
test( x, &X::f);
test(&x, &X::f);
test(42, 1337);
}
Output:
Reference overload called
Pointer overload called
Catch-all overload called
As you can see, in the third call of test, substitution fails without errors.
C++17 will probably provide a generic means to query for features. See N4502 for details, but as a self-contained example consider the following.
This part is the constant part, put it in a header.
// See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf.
template <typename...>
using void_t = void;
// Primary template handles all types not supporting the operation.
template <typename, template <typename> class, typename = void_t<>>
struct detect : std::false_type {};
// Specialization recognizes/validates only types supporting the archetype.
template <typename T, template <typename> class Op>
struct detect<T, Op, void_t<Op<T>>> : std::true_type {};
The following example, taken from N4502, shows the usage:
// Archetypal expression for assignment operation.
template <typename T>
using assign_t = decltype(std::declval<T&>() = std::declval<T const &>())
// Trait corresponding to that archetype.
template <typename T>
using is_assignable = detect<T, assign_t>;
Compared to the other implementations, this one is fairly simple: a reduced set of tools (void_t and detect) suffices. Besides, it was reported (see N4502) that it is measurably more efficient (compile-time and compiler memory consumption) than previous approaches.
Here is a live example, which includes portability tweaks for GCC pre 5.1.
Here is one good article of SFINAE: An introduction to C++'s SFINAE concept: compile-time introspection of a class member.
Summary it as following:
/*
The compiler will try this overload since it's less generic than the variadic.
T will be replace by int which gives us void f(const int& t, int::iterator* b = nullptr);
int doesn't have an iterator sub-type, but the compiler doesn't throw a bunch of errors.
It simply tries the next overload.
*/
template <typename T> void f(const T& t, typename T::iterator* it = nullptr) { }
// The sink-hole.
void f(...) { }
f(1); // Calls void f(...) { }
template<bool B, class T = void> // Default template version.
struct enable_if {}; // This struct doesn't define "type" and the substitution will fail if you try to access it.
template<class T> // A specialisation used if the expression is true.
struct enable_if<true, T> { typedef T type; }; // This struct do have a "type" and won't fail on access.
template <class T> typename enable_if<hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
return obj.serialize();
}
template <class T> typename enable_if<!hasSerialize<T>::value, std::string>::type serialize(const T& obj)
{
return to_string(obj);
}
declval is an utility that gives you a "fake reference" to an object of a type that couldn't be easily construct. declval is really handy for our SFINAE constructions.
struct Default {
int foo() const {return 1;}
};
struct NonDefault {
NonDefault(const NonDefault&) {}
int foo() const {return 1;}
};
int main()
{
decltype(Default().foo()) n1 = 1; // int n1
// decltype(NonDefault().foo()) n2 = n1; // error: no default constructor
decltype(std::declval<NonDefault>().foo()) n2 = n1; // int n2
std::cout << "n2 = " << n2 << '\n';
}
The following code uses SFINAE to let compiler select an overload based on whether a type has certain method or not:
#include <iostream>
template<typename T>
void do_something(const T& value, decltype(value.get_int()) = 0) {
std::cout << "Int: " << value.get_int() << std::endl;
}
template<typename T>
void do_something(const T& value, decltype(value.get_float()) = 0) {
std::cout << "Float: " << value.get_float() << std::endl;
}
struct FloatItem {
float get_float() const {
return 1.0f;
}
};
struct IntItem {
int get_int() const {
return -1;
}
};
struct UniversalItem : public IntItem, public FloatItem {};
int main() {
do_something(FloatItem{});
do_something(IntItem{});
// the following fails because template substitution
// leads to ambiguity
// do_something(UniversalItem{});
return 0;
}
Output:
Float: 1
Int: -1
Here, I am using template function overloading (not directly SFINAE) to determine whether a pointer is a function or member class pointer: (Is possible to fix the iostream cout/cerr member function pointers being printed as 1 or true?)
https://godbolt.org/z/c2NmzR
#include<iostream>
template<typename Return, typename... Args>
constexpr bool is_function_pointer(Return(*pointer)(Args...)) {
return true;
}
template<typename Return, typename ClassType, typename... Args>
constexpr bool is_function_pointer(Return(ClassType::*pointer)(Args...)) {
return true;
}
template<typename... Args>
constexpr bool is_function_pointer(Args...) {
return false;
}
struct test_debugger { void var() {} };
void fun_void_void(){};
void fun_void_double(double d){};
double fun_double_double(double d){return d;}
int main(void) {
int* var;
std::cout << std::boolalpha;
std::cout << "0. " << is_function_pointer(var) << std::endl;
std::cout << "1. " << is_function_pointer(fun_void_void) << std::endl;
std::cout << "2. " << is_function_pointer(fun_void_double) << std::endl;
std::cout << "3. " << is_function_pointer(fun_double_double) << std::endl;
std::cout << "4. " << is_function_pointer(&test_debugger::var) << std::endl;
return 0;
}
Prints
0. false
1. true
2. true
3. true
4. true
As the code is, it could (depending on the compiler "good" will) generate a run time call to a function which will return true or false. If you would like to force the is_function_pointer(var) to evaluate at compile type (no function calls performed at run time), you can use the constexpr variable trick:
constexpr bool ispointer = is_function_pointer(var);
std::cout << "ispointer " << ispointer << std::endl;
By the C++ standard, all constexpr variables are guaranteed to be evaluated at compile time (Computing length of a C string at compile time. Is this really a constexpr?).

Getting value of template parameter from embracing type

Say, I have a template class with an integer parameter:
template <int N>
class A
{
public:
static int get_N()
{
return N;
}
};
template<typename T>
class B
{
public:
B()
{
cout << "N = " << T::get_N() << endl; // Accessing N via the auxiliary method
}
};
To reference the N template parameter in class B I had to create an auxiliary method in A. I would like to do something like this:
template <int N>
class A
{
};
template<typename T>
class B
{
public:
B()
{
cout << "N = " << T::N << endl; // Accessing N directly
}
};
The problem is that I'm going to have a lot of A template specializations and I don't really want to copy this auxiliary method to all of specialized classes and i don't want to introduce inheritance for this.
Is it possible to achieve what I want?
You could deduce the value from a specialization:
#include <iostream>
template <typename T> struct get_N;
template <template <int N> class T, int N>
struct get_N<T<N>> {
static constexpr int value = N;
};
template <int N> struct A {};
template <typename T>
struct B {
void f() { std::cout << get_N<T>::value << '\n'; }
};
int main() {
B<A<10>>().f();
}
You can extract N like this:
template<typename A>
struct get_N;
template<int N>
struct get_N<A<N> > : std::integral_constant<int,N> { };
This way you don't need to define anything within each A specialization, yet you can say e.g.
using X = A<3>;
cout << "N = " << get_N<X>() << endl; // prints: N = 3
However, I might still prefer to let A derive a lightweight template class that only defines a static constrexpr variable as in juanchopanza's answer. Then each A specialization would look like
template<>
struct A<3> : A_base<3> { ... };
which is not too bad. In fact, looking at both options again, I see that A_base is nothing more than
template<int N>
using A_base = std::integral_constant<int,N>;
so it could be given a more generic short name. I usually call it num.