Parent template argument deduction in nested class constructor - c++

I am trying to write the "promotion" constructor of a nested class that can deduce the parent class template. It works fine for the parent class, but not in the nested class. Here is a code example.
template <class T>
struct potato {
struct baked {
template <class O>
baked(const typename potato<O>::baked& p)
: something(static_cast<T>(p.something)) {
}
baked() = default;
T something;
};
template <class O>
potato(const potato<O>& p)
: mybaked(p.mybaked) {
}
potato() = default;
baked mybaked;
};
int main(int, char**) {
potato<int> potato1;
potato<short> potato2(potato1);
}
Is this legal?
Various compilers output various errors. Clang has the most readable in my mind. It states :
candidate template ignored: couldn't infer template argument 'O'
https://godbolt.org/z/y_IZiE
So I'm guessing either I've messed up the signature, or this isn't a c++ supported feature.

I don't know of any way to deduce the template argument T for a baked's parent potato<T>. You can know T using decltype(p.something) but that doesn't seem to help solve the problem with calling the constructor. One workaround is to change baked's constructor to take any O and assume it has a something :
struct baked {
template <class O>
baked(const O & p) : something(static_cast<T>(p.something))
{ }
baked() = default;
T something;
};
This will work but it is less type-safe than your original code seems to intend. One workaround for that problem could be to introduce a static_assert that checks that O is actually a potato<U>::baked :
#include <type_traits>
template <class T>
struct potato {
struct baked {
template <class O>
baked(const O & p) : something(static_cast<T>(p.something))
{
using t_parent = potato<decltype(p.something)>;
static_assert(std::is_same<O, typename t_parent::baked>::value, "Not a baked potato!");
}
baked() = default;
T something;
};
template <class O>
potato(const potato<O>& p)
: mybaked(p.mybaked) {
}
potato() = default;
baked mybaked;
};
This should compile fine for the intended usage but fail with "Not a baked potato!" if you try to pass anything else with a something. This would fail :
struct foo {
int something = 0;
};
int main(int, char**) {
foo bar;
potato<int>::baked baz(bar); // Error: Not a baked potato!
}

As state by the compiler O is not deducible from const typename potato<O>::baked& (on left side of ::).
You have several workarounds:
Move baked outside parent and make it template:
// Possibly in namespace details
template <typename T>
struct baked_impl {
template <class O>
baked_impl(const typename baked_impl<O>& p)
: something(static_cast<T>(p.something)) {
}
baked_impl() = default;
T something;
};
template <class T>
struct potato {
using baked = baked_impl<T>;
// ...
};
Add parent info in baked and use SFINAE:
template <class T> struct potato;
// traits for SFINAE
template <class T> struct is_potato : std::false_type {};
template <class T> struct is_potato<potato<T>> : std::true_type {};
template <class T>
struct potato {
using value_type = T;
struct baked {
using parent = potato;
template <class O, std::enable_if_t<is_potato<typename O::parent>::value, int> = 0>
baked(const O& p)
: something(static_cast<typename O::parent::value_type>(p.something)) {
}
baked() = default;
T something;
};
// ...
};

Related

Make new instance of the template class of a c++ object

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
};

Get deepest class in CRTP inheritance chain

I would like to know how to solve the following problem (C++17):
suppose there are several template classes, inherited from each other in CRTP-like fashion (single inheritance only). For a given instantiated template base class, find the class that is furthest from it down the inheritance chain.
I first thought that is should be pretty easy, but was not able to accomplish this.
To simplify, suppose that every root and every intermediate class has using DerivedT = Derived in its public area.
Example:
template <class T>
struct GetDeepest {
using Type = ...;
};
template <class T>
struct A {
using DerivedT = T;
};
template <class T>
struct B : public A<B<T>> {
using DerivedT = T;
};
struct C : B<C> {
};
struct D : A<D> {
};
GetDeepest<A<D>>::Type == D;
GetDeepest<B<C>>::Type == C;
GetDeepest<A<B<C>>>::Type == C;
...
First implementation I've tried:
template <class T>
struct GetDeepest {
template <class Test, class = typename Test::DerivedT>
static std::true_type Helper(const Test&);
static std::false_type Helper(...);
using HelperType = decltype(Helper(std::declval<T>()));
using Type = std::conditional_t<std::is_same_v<std::true_type, HelperType>,
GetDeepest<typename T::DerivedT>::Type,
T>;
};
Second implementation I've tried:
template <class T>
struct HasNext {
template <class Test, class = typename Test::DerivedT>
static std::true_type Helper(const Test&);
static std::false_type Helper(...);
using HelperType = decltype(Helper(std::declval<T>()));
static const bool value = std::is_same_v<std::true_type, HelperType>;
};
template <class T>
auto GetDeepestHelper(const T& val) {
if constexpr(HasNext<T>::value) {
return GetDeepestHelper(std::declval<typename T::DerivedT>());
} else {
return val;
}
}
template <class T>
struct GetDeepest {
using Type = decltype(GetDeepestLevelHelper(std::declval<T>()));
};
None of them compile.
First one -- because of incomplete type of GetDeepest<T> in statement using Type = ..., second because of recursive call of a function with auto as a return type.
Is it even possible to implement GetDeepest<T> class with such properties? Now I'm very curious, even if it might be not the best way to accomplish what I want.
Thanks!
I'm not sure if I fully understand the question so feel free to ask me in comments.
But I think this should work:
#include <type_traits>
template<typename T>
struct GetDeepest
{
using Type = T;
};
template<template<typename> class DT, typename T>
struct GetDeepest<DT<T>>
{
using Type = typename GetDeepest<T>::Type;
};
template <class T>
struct A {
using DerivedT = T;
};
template <class T>
struct B : public A<B<T>> {
using DerivedT = T;
};
struct C : B<C> {
};
struct D : A<D> {
};
int main()
{
static_assert(std::is_same<GetDeepest<A<D>>::Type, D>::value);
static_assert(std::is_same<GetDeepest<B<C>>::Type, C>::value);
static_assert(std::is_same<GetDeepest<A<B<C>>>::Type, C>::value);
}

Lazy-evaluate dependent-types (CRTP) [duplicate]

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; }
};

enable_if and constructor with VS2012

I am trying to conditionally enable a constructor template. With a fully C++11-compliant compiler, I know how to do this using an extra default template argument. However, I need to support VS2012, which has std::enable_if but does not support defaulted function template arguments.
With C++11, I would write the following:
template<typename T>
struct Class
{
template<typename O,
typename = typename std::enable_if<std::is_convertible<O*, T*>::value>::type>
Class(O*) {}
};
I tried the following, but it gives an error C4336 and various follow-up errors:
template<typename T>
struct Class
{
template <typename O>
Class(O*, typename std::enable_if<std::is_convertible<O*, T*>::value>::type *= nullptr)
{
}
};
Is there any way to make this work with VS2012?
Addition:
The usage of the class would be as follows:
struct X { };
struct X2 : X { };
struct Y { };
struct Client
{
Client(Class<X> x) {}
Client(Class<Y> y) {}
};
void test() {
X2* x2;
Client client(x2); // error C2668: ambiguous call to overloaded function
// (without std::enable_if)
}
You are so close to the solution !
template<typename T>
struct Class
{
template <typename O>
Class(O*, typename std::enable_if<std::is_convertible<O*, T*>::value>::type * = nullptr)
{
}
};
Did you spot the difference ? *= inside the parameter list was parsed as the multiplication/assignment operator, not as a pointer type followed by a default argument. Hence, syntax errors.
This is because the C++ parser is specified to consume as many characters as possible when it forms a token (the so-called Maximum Munch Rule). Adding a space splits it into two separate tokens, as intended.
I am afraid, you'd have to use a helper construct function (I didn't find a way around it). But something like this should work:
#include <type_traits>
template<typename T>
struct Class
{
template<typename O>
Class(O* o) { construct(o, std::integral_constant<bool, std::is_convertible<T*, O*>::value>()); }
template<class O>
void construct(O*, std::true_type ) { /* convertible */ }
template<class O>
void construct(O*, ... ) { /* not convertible */ }
};
struct X { };
struct Y : public X { };
void check() {
X x;
int i;
Class<Y> cl(&x);
Class<Y> cl1(&i);
}
Since C++11, you can also use delegating constructors to do that:
template<typename T>
class Class {
template<typename O>
Class(O *o, std::true_type) {}
template<typename O>
Class(O *o, std::false_type) {}
public:
template<typename O>
Class(O *o): Class(o, typename std::is_convertible<O*, T*>::type) {}
};
The basic idea is the one of tag dispatching and maybe it works fine also with VS2012.
See here for further details.

Is there an idiom/design pattern for restricting templates?

How do one restrict the typename T to specific type?
Consider this:
template <typename T>
struct Worker {
// T can only be certain type allowing specific functionality.
// i.e T needs to be a product of some interface, support some functions, say T::toString(), T::print(), T::get().
// Do something with T
};
This is what I usually end up doing:
struct WorkableType {
std::string toString() { return ""; }
int get() { return 0;}
}
struct WorkabelTypeA : WorkableType {
std::string toString() { return "A"; }
int get() { return 1;}
};
//Similarly
struct WorkableTypeB : WorkableType;
And use static assert and std::is_base_of:
template <typename T>
struct Worker {
static_assert(std::is_base_of<WorkableType, T>::value, "Needs workable type");
// Do something with T
};
Is there any other design pattern, a more C++ way to restrict accidental instantiation of bad typed templates?
Edit: Seems like this would be better solved with C++ Concepts when it becomes the standard. Until then i guess, static_assert is probably more cleaner and verbose than enable_if.
You could use SFINAE and template specialisation:
// type trait that evaluates always to false to use in the primary template
template<typename ... T> struct always_false : std::false_type { };
// primary template
template<typename T, typename Enable = void>
struct Worker {
static_assert(always_false<T, Enable>::value, "Needs workable type");
};
// specialisation
template<typename T>
struct Worker<T, std::enable_if_t<std::is_base_of<WorkableType, T>::value>> {
...
};
You can create traits and check that in your class, So, no need of inheritance. For example:
template <typename T>
using toString_t = decltype(std::declval<T>().toString());
template <typename T>
using get_t = decltype(std::declval<T>().get());
// Use C++17, but can be done in C++11
template <typename T>
using has_toString = std::is_detected<toString_t, T>;
template <typename T>
using has_get = std::is_detected<get_t, T>;
And then
template <typename T>
struct Worker {
static_assert(has_toString<T>::value, "T should have toString");
static_assert(has_get<T>::value, "T should have get");
};
Demo
If you know exactly which types you want to allow, then a traits class is a succinct way to do it:
#include <utility>
// by default nothing is workable
template<class T>
struct is_workable : std::false_type
{
};
template <typename T>
struct Worker {
static_assert(is_workable<T>(), "not a workable type");
// T can only be certain type allowing specific functionality.
// i.e T needs to be a product of some interface, support some functions, say T::toString(), T::print(), T::get().
// Do something with T
};
// define a worker
struct A {};
// make it workable
template<> struct is_workable<A> : std::true_type {};
// define another worker but forget to make it workable
struct B {};
int main()
{
Worker<A> wa{};
// Worker<B> wb{}; // compile error - not workable
};
You can use specializations as it follows:
#include<string>
struct WorkableType {
std::string toString() { return ""; }
int get() { return 0; }
};
struct A {};
struct B {};
template<typename> struct Worker;
template<> struct Worker<A>: WorkableType {};
int main() {
Worker<A> wa;
// this won't compile
// Worker<B> wb;
}