This question already has answers here:
invalid use of incomplete type
(5 answers)
Closed 4 years ago.
I'd like the following piece of code to work:
template <typename Self>
struct foo_base {
auto get(typename Self::type n) { return n; }
};
template <typename T>
struct foo : public foo_base<foo<T>> {
using type = T;
};
The problem of course is that the base is instantiated first so you cannot refer to the derived member types. I'd need some kind of lazy-evaluation here.
I've tried to make the function template and have SFINAE on it, something like:
template <typename Self>
struct foo_base {
template <typename T, typename = std::enable_if_t<std::is_same_v<T, typename Self::type>>>
auto get(T n) { return n; }
};
but it doesn't seem to affect the order. Any ideas?
Edit:
Constraints of solution:
I can't pass the type as a template parameter from the derived class.
Main reason is: The type is very complicated to construct, several
hundred characters. So can't do something like struct foo : foo_base<foo<T>, T> or variants.
I need to constraint the function to that type, I
can't check inside the function. Maybe there are overloads in the
derived class.
You might create external traits, something like:
template <template T>
struct TypeT;
template <typename Self>
struct foo_base {
auto get(typename TypeT<Self>::type n) { return n; }
};
template <typename T> struct foo;
template <template T>
struct TypeT<foo<T>> {
using type = T; // Don't use foo<T>::type, so traits can be used with incomplete type
};
template <typename T>
struct foo : public foo_base<foo<T>> {
using type = typename TypeT<foo>::type; // Or directly = T
};
Else you might indeed use SFINAE, but you must wait the type to be complete (when instantiating the method works in your case),
as for example:
template <typename Self>
struct foo_base
{
template <typename T = Self>
auto get(typename T::type n) { return n; }
};
Related
So if I have
template <class T>
class Object {
// stuff
};
and I receive an instance of object in a function I want to call the constructor of class T.
void foo(Object object) {
auto newT = object::T();
}
Is this possible?
Typically the best solution is to template the inner type:
template <class T>
void foo(Object<T> object) {
T newT;
}
However, sometimes (with more meta-programming) this sort of solution will be more verbose than the alternatives:
Option 1: store the template variable in the Object class:
template <class T>
class Object {
// stuff
public:
using inner_type = T;
};
Then you can access the template type like so:
template <class Obj>
void foo(Obj object) {
typename Obj::inner_type newT;
}
Option 2: make a type trait (no need to add inner_type to Object):
template <class T>
struct tag {
using type = T;
};
template <class>
struct inner;
template <template <class> class S, class T>
struct inner <S<T>> : tag<T> {};
template <typename T>
using inner_t = typename inner<T>::type;
Which you can then use like so:
template <class Obj>
void foo(Obj object) {
inner_t<Obj> newT;
}
It's probably best to generalise inner_type to take the first inner argument so that it could handle template types with more arguments, like std::vector (second argument has a default):
template <class>
struct front;
template <template <class, class...> class R, class S, class ... Ts>
struct front <R<S, Ts...>> : tag<S> {};
template <typename T>
using front_t = typename front<T>::type;
Demo
No, you create an instance of a class template by specializing the template. You do this by putting the type you want to use in angle brackets:
void foo(Object<int> object) {
// auto newobj = Object<int>(); this will work
Object<int> newobj; // but this has less cruft
}
or
template <class U>
void foo(Object<U> object) {
// auto newobj = Object<U>();
Object<U> newobj {object};
}
Remember that the symbol T does not exist outside the template definition. To get a "real" Object you have to put in an actual type. I chose int but you will probably use something else.
Of course, this will only work if the stuff contains a corresponding constructor:
template <class T>
class Object {
// stuff
public:
Object(); // often implicit but sometimes not
Object(Object<T> const &i) = default;
// more stuff
};
Consider the code below:
template <typename T>
class A{
...
}
template <class U>
class B{
...
}
int main {
B<A<int>> a;
...
}
How can I get the template parameter of A (int in this case) inside B, if A<int> is the template parameter for B?
I could parametrize B as follows, but I feel like I am sending an unnecessary piece of information.
template <class AA, typename T>
class B { ... }
The reason I do not simply use template <typename T> for class B is that I have a pointer to class A inside B, and I want to use the template parameter class AA to see if that pointer is const or not, hence have the correct type for the member pointer in B.
There are several ways, depending of that you might change:
Quick way, specialize B
template <class> class B;
template <class T>
class B<A<T>>
{
// Use directly T
//...
};
Add info in A directly (as std containers do with value_type)
template <typename T>
struct A
{
using my_type = T;
};
// Then in `B<U>`, use `typename U::my_type`
Use external traits to extract information from A (as std::iterator_traits) (that also allows to handle built-in types):
template <typename T>
struct ATrait;
template <typename T>
struct ATrait<A<T>>
{
using my_type = T;
};
// Possibly a generic one
template <template <typename> class C, typename T>
struct ATrait<C<T>>
{
using my_type = T;
};
// Then in `B<U>`, use `typename ATrait<U>::my_type`
I think the following does what you want:
#include<type_traits>
template<class>
class A{};
template<class>
struct get_inner;
template<template<class> class TT, class T>
struct get_inner<TT<T>> {
using outer = TT<T>;
using inner = T;
};
template<class TT>
struct B {
using A = TT;
using inner = typename get_inner<std::decay_t<A>>::inner;
};
int main(int argc, char *argv[])
{
static_assert(std::is_const_v<typename B<const A<int>>::A>);
static_assert(!std::is_const_v<typename B<A<int>>::A>);
}
Note the std::decay_t, it wouldn't work with the const parameter directly (hence we cannot just specialize B in this way). Maybe decay_t is a bit strong but it works^^
Try this
template <typename X> class B;
template <template <typename> class XX, typename T>
class B<XX<T>>
{
// your implementation
};
B<A<int>> a;
Is it possible to write a metafunction that, given a type with several occurrences of certain type template<class> class Decor, returns the type without the appearances of the class Decorator.
An example would be to convert following type
A<Decor<T<B<A<Decor<C>>>>>>
into
A<T<B<A<C>>>>
We assume that the structure of the final type is indeed a correct type, but we do not assume anything on the structure of the input type. It could be the case that some types used to construct the input type were of the form template<class...> class or any other type class.
You can use a class template and a couple of specializations like these:
template<typename T>
struct RemDec {
using type = T;
};
template<template<typename...> class C, typename... T>
struct RemDec<C<T...>> {
using type = C<typename RemDec<T>::type...>;
};
template<typename T>
struct RemDec<Decorator<T>> {
using type = typename RemDec<T>::type;
};
The class template helps to stop iterating over your chain of types.
The first specialization memorizes a class template and helps cleaning up what remains.
The last specialization removes the Decorator detected and goes on analyzing what remains.
It follows a minimal, working example:
#include<type_traits>
template<typename>
struct Decorator {};
template<typename...>
struct S {};
template<typename T>
struct RemDec {
using type = T;
};
template<template<typename...> class C, typename... T>
struct RemDec<C<T...>> {
using type = C<typename RemDec<T>::type...>;
};
template<typename T>
struct RemDec<Decorator<T>> {
using type = typename RemDec<T>::type;
};
int main() {
static_assert(std::is_same<
typename RemDec<S<Decorator<S<S<Decorator<S<int>>>>>, Decorator<S<double>>>>::type,
S<S<S<S<int>>>, S<double>>
>::value, "!");
}
As you can see by running it, any instance of Decorator is removed from the original type.
template <class T>
struct RemDec
{ using type = T; };
template <class T>
struct RemDec<Decor<T>>
{ using type = T; };
template <class T>
struct RemDec<T&>
{ using type = typename RemDec<T>::type&; };
template <class T>
struct RemDec<T&&>
{ using type = typename RemDec<T>::type&&; };
template <class T>
struct RemDec<const T>
{ using type = typename RemDec<T>::type const; };
template <class T>
struct RemDec<volatile T>
{ using type = typename RemDec<T>::type volatile; };
template <template <typename...> class TT, class... Ts>
struct RemDec<TT<Ts...>>
{ using type = TT<typename RemDec<Ts>::type...>; }
You will need even more specializations if your templates might have value- or template-template-arguments.
Say I have some template type...
template <typename T> struct Foo {
Foo(T t) {}
};
Is there a way to pass a specified Foo type to a function so that the function has direct visibility of T?
Ideally I would be able to write something like this...
Foo<int> foo = create<Foo<int>>();
The closest I've been able to come is
template <
template <typename> typename TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
which would then be used like
Foo<int> foo = create<Foo, int>();
Thanks for any help.
This form of template template parameter is only allowed in C++17:
template < // v---------- typename here not allowed
template <typename> typename TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
You must replace the typename pointed out by class:
template < // v---------- class allowed
template <typename> class TT,
typename T,
std::enable_if_t<std::is_same<TT<T>, Foo<T>>::value, int> = 0
>
Foo<T> create() {
return Foo<T>(T());
}
In C++17, both compiles and are equivalent.
To make your syntax Foo<int> foo = create<Foo<int>>(); work, you simply need to do this:
template <typename T>
T create() {
return T{};
}
If you want to limit what type can be sent, you must create a type trait:
// default case has no typedef
template<typename>
struct first_param {};
// when a template is sent, define the typedef `type` to be equal to T
template<template<typename> class TT, typename T>
struct first_param<TT<T>> {
using type = T;
};
// template alias to omit `typename` everywhere we want to use the trait.
template<typename T>
using first_param_t = typename first_param<T>::type;
Then, use your trait:
template <
typename T,
void_t<first_param_t<T>>* = nullptr
> // ^---- if the typedef is not defined, it's a subtitution error.
T create() {
return T(first_param_t<T>{});
}
You can implement void_t like this:
template<typename...>
using void_t = void;
Live at Coliru
One simple way is to add the sub-type information in Foo directly:
template <typename T> struct Foo {
using type = T;
Foo(T t) {}
};
and then
template <typename FooT>
FooT create() {
return FooT(typename FooT::type{});
}
You might add SFINAE if you want:
template <typename FooT>
auto create()
-> decltype(FooT(typename FooT::type{}))
{
return FooT(typename FooT::type{});
}
If you want really restrict the function to Foo exclusively, you have to create a traits and SFINAE on it.
Why not simply use a tag dispatching, e.g.:
template <class>
struct tag { };
template <class T>
Foo<T> create(tag<Foo<T>>) {
return Foo<T>(T());
}
//...
Foo<int> foo = create(tag<Foo<int>>{});
In C++11
Demo
The gist is to have an entry point function named create that can instantiate a create_helper struct to create the proper type.
We can create our structures using template specialization so that we're forcing a templated class to be passed.
Full code:
template<class T>
struct create_helper
{
static_assert(sizeof(T) == 0, "Need to pass templated type to create");
};
template <class T, template<class> class TT>
struct create_helper<TT<T>>
{
static TT<T> apply()
{
return {T{}};
}
};
template<class T>
auto create() -> decltype(create_helper<T>::apply())
{
return create_helper<T>::apply();
}
And a test:
template<class T>
struct Foo
{
Foo(T t){std::cout << "Constructed Foo with value " << t << std::endl;}
};
int main()
{
Foo<int> foo = create<Foo<int>>();
}
Output:
Constructed Foo with value 0
I have a primary template, which I need to specialize based on based on a meta-function. The usual idiom is like
template<class T,class E = void>
struct foo { };
template<class T>
struct foo<T,std::enable_if_t<is_xxx<T>{}> > {};
However, I have a situation where the primary template is written as
template<class T>
struct foo { };
(i.e. without that extra SFINAE placeholder) and I am not allowed to change it. What is the best way to specialize it based on a trait( like I can do that mostly for function template based on return type or additional argument)?
You can add a base class:
template<class T, class E = void>
struct foo_base { };
template<class T>
struct foo : foo_base<T, std::enable_if_t<is_xxx<T>{}>> { };
Then you move any members of foo into foo_base.