Reputation: 197
So i have problem with code like this:
I have struct like this
template <int N>
struct Inner
{
enum
{
val = 2*N
};
};
And i want to achive sth like this:
int v = Outer<Inner<4>>::val;
int b = Outer<false>::val;
cout<< v <<endl;
cout<< b <<endl;
My goal is to created "Outer
" struct which takes bool
or Inner<int N>
and set Outer::val
to Inner::val
or bool
So i have created sth like this (not working):
template <bool B>
struct Outer
{
enum
{
val = B
};
};
template <Inner<int> I>
struct Outer
{
enum
{
val = I::val
};
};
Whats wrong with this and how to fix that? (I have seen some similar questions, but still can't apply this to my problem)
Upvotes: 2
Views: 1194
Reputation: 66200
There are some problems in your code.
First of all: you define two different Outer
structs
template <bool B>
struct Outer
{ /* ... */ };
template <Inner<int> I>
struct Outer
{ /* ... */ };
And you can't.
If you want, you can declare an Outer
struct and two specializations, but you have to decide what type of template argument Outer
has to receive.
Because, looking at your desiderata,
int v = Outer<Inner<4>>::val;
int b = Outer<false>::val;
you want pass to it a type in one case (Inner<4>
) and a value in the other case. And you can't.
You have to decide if Outer
receive a type or a value. Before C++17, if receive a value, you have to decide the type of the value; starting from C++17, you can declare Outer
as receiving a value of a generic type (auto
as type of the value).
Problem: a value of Inner<int>
can't be a template parameter (but see also the Michael Kenzel's answer, that show a possible C++20 solution based on template values arguments).
So the only solution I see (before C++20) is declare Outer
as receiving a type
template <typename>
struct Outer;
Then you can define a Outer
specialization for Inner
types
template <int N>
struct Outer<Inner<N>>
{ enum { val = Inner<N>::val }; }; // or simply enum { val = N };
For bool
values, you have to wrap they in a class; I suggest (starting from C++11) the use of the standard class std::integral_constant
and the definition of the following Outer
specialization
template <bool B>
struct Outer<std::integral_constant<bool, B>>
{ enum { val = B }; };
The use is as follows
int v = Outer<Inner<4>>::val;
int b = Outer<std::integral_constant<bool, false>>::val;
std::cout << v << std::endl;
std::cout << b << std::endl;
You can also use std::false_type
defining b
int b = Outer<std::false_type>::val;
and, starting from C++17, also std::bool_constant
(a shorthand for std::integral_constant
for bool
values)
int b = Outer<std::bool_constant<false>>::val;
Upvotes: 2
Reputation: 15943
A template parameter can be either a type, a value (non-type), or a template [temp.param]. What you're trying to achieve would require your template Outer
to have a parameter that can be either a type or a value. Unfortunately, this is not possible.
What you could do is wrap your bool value in a type:
template <bool b>
struct InnerBoolean
{
static constexpr bool val = b;
};
and then have one common definition for Outer
template <typename T>
struct Outer
{
enum
{
value = T::val
};
};
and then use Outer<Inner<4>>
and Outer<InnerBoolean<False>>
.
Rather than write your own wrapper, if you rename val
to value
, you can use the wrappers that the standard library provides in std::bool_constant
and std::true_type
and std::false_type
.
While up to C++17, a non-type template parameter cannot be of class type [temp.param]/4, C++20 will lift this restriction and allow template parameters of any literal type. Thus, as long as Inner
can be a literal type, you will be able to just pass a value of type Inner
directly and use an auto template parameter:
struct Inner
{
int N;
constexpr Inner(int N) : N(N) {}
constexpr operator int() const { return 2*N; }
};
template <auto val>
struct Outer
{
enum
{
value = val
};
};
auto a = Outer<Inner(4)>::value;
auto c = Outer<false>::value;
Upvotes: 2