I am trying to use std::conditional to implement static inheritance.
I have two possible parents of my child class, parent_one, which should hold multiple variables based on passed types, and parent_two, which takes two types. I am using tag dispatching to differ between the classes I want to inherit from.
Now to the problem. When I am calling child and tagging it to inherit from parent_one with two types, it works as intended. However, if I try to pass any number of types into the child with the intention of inheriting from parent_one, I get error:
static_polymorphism.cpp: In instantiation of ‘class child<foo_type, int, int, double, float>’:
static_polymorphism.cpp:110:41: required from here
static_polymorphism.cpp:99:7: error: wrong number of template arguments (4, should be 2)
99 | class child : public std::conditional_t<
| ^~~~~
static_polymorphism.cpp:90:7: note: provided for ‘template<class T, class F> class parent_two’
90 | class parent_two {
| ^~~~~~~~~~
static_polymorphism.cpp: In function ‘int main(int, char**)’:
static_polymorphism.cpp:111:9: error: ‘class child<foo_type, int, int, double, float>’ has no member named ‘log’
111 | first.log();
If I understand that correctly the compiler should generate code based on my tag dispatching. That means it should create overloaded classes - N(Based on types passed) from parent_one and M(Based on types passed as well) from parent_two. However for some reason, unknown to me, it is not accepting the variable count of types. Could you tell me what am I doing wrong please?
Implementation is here.
using one_t = struct foo_type{};
using two_t = struct bar_type{};
template <typename ... TYPES>
class parent_one {
public:
parent_one() = default;
void log() {
std::cout << "parent_one" << std::endl;
}
};
template <typename T, typename F>
class parent_two {
public:
parent_two() = default;
void log() {
std::cout << "parent_two" << std::endl;
}
};
template <typename T, typename ... ARGS>
class child : public std::conditional_t<
std::is_same_v<T, one_t>,
parent_one<ARGS...>,
parent_two<ARGS...>
>
{
public:
child() = default;
};
int main(int argc, char *argv[]) {
child<one_t, int, int, double, float> first;
first.log();
child<two_t, int, int> second;
second.log();
return 0;
}
std::conditional_t<
std::is_same_v<T, one_t>,
parent_one<ARGS...>,
parent_two<ARGS...>
>
Here both alternatives are validated before the condition is checked. std::conditional_t is not magical, being just a regular template it requires all template arguments to be valid before it can do anything.
You need to delay the substitution of template arguments into a parent template until after one of the alternatives is selected. Here's one possible solution:
template <template <typename...> typename T>
struct delay
{
template <typename ...P>
using type = T<P...>;
};
// ...
class child :
public std::conditional_t<
std::is_same_v<T, one_t>,
delay<parent_one>,
delay<parent_two>
>::template type<ARGS...>
{
// ...
};
You may be classic:
template<class T, class... Args> struct child: parent_one<Args...> {};
template<class T, class A, class B> struct child<T, A, B>: parent_two<A, B> {};
template<class A, class B> struct child<one_t, A, B>: parent_one<A, B> {};
(the two specializations can be combined into one with requires (!std::is_same_v<T, one_t>)).
Related
Similarily to this question is it possible to use SFINAE to determine if a type has a member function with a certain argument(s)? Without the argument list as answered in the other question and the std::void_t example says it works fine. (I stood with the latter.) However, If I try to add a check for argument lust with extra std::decltype<>() it fails with template parameters not deducible in partial specialization. Is there any way to extend this method to check for certain argument types?
Example code:
#include <type_traits>
class A {
public :
void a(){}
void b(int val){}
};
class B {
public :
void b(float val){}
};
// --- Has function a() without arguments
template <typename T, typename = void> struct has_a : std::false_type
{
};
template <typename T> struct has_a<T, std::void_t<decltype(std::declval<T>().a())>> : std::true_type
{
};
template <typename T> constexpr bool has_a_v = has_a<T>::value;
// This is OK:
static_assert(true == has_a_v<A>);
static_assert(false == has_a_v<B>);
// --- Has function b() with one argument of one given type
template <typename T, typename U, typename = void> struct has_b : std::false_type
{
};
template <typename T ,typename U> struct has_b<T, std::void_t<decltype(std::declval<T>().b(std::declval<U>()))>> : std::true_type
{
};
template <typename T, typename U> constexpr bool has_b_v = has_b<T, U>::value;
// This fails with `template parameters not deducible in partial specialization`:
static_assert(true == has_b_v<A, int>);
static_assert(false == has_b_v<B, int>);
static_assert(false == has_b_v<A, float>);
static_assert(true == has_b_v<B, float>);
int main () { return 0;}
Yes. Example of a void_t check for member function b in class B:
decltype( static_cast<void(B::*)(float)>(&B::b) )
This is if you want to check for exact signature. Your way is fine, too (once you fix it as commented under the question), but it actually checks if member function is callable with certain types of arguments (and ignores the return type).
In your partial specialization, you forgot to add the U parameter:
template <typename T, typename U>
struct has_b<
T,
U, // You forgot it
std::void_t<decltype(std::declval<T>().b(std::declval<U>()))>> : std::true_type
{
};
I have very long parameter pack. I wonder is there any way to store the parameter pack and reuse it later. For example, if there are 2 templates:
template<class ... Types> struct A {};
template<class ... Types> struct B {};
I have a specialized type:
typedef A<int, int, float> A1_t;
Is there any operation can let me create a specialized type B which use the same parameter pack as A1_t? (B<int, int, float>). Is there any method to retrieve the <int, int, float> from A1_t or store it?
I want obtain a specialized type B1_t instead of creating the object of B1_t.
A and B describes completely different concept, so I cannot make B nested inside A.
moreover, I would also like to feed the parameter packs to specialize function templates.
template<class ...Ts>
C<Ts...> func1()
{
}
so I can directly call func1<int, int, float>()
It will be nice if I can do something like this:
template<typename T>
transform<B, T> func1()
{
}
next step would be something similar to this:
template<template<class...Ts> templ>
B<Ts...> func2(Ts ...args)
{
}
So I can do func2<A1_t>(1, 2, 3.0f) directly.
Something like this? Using a type transformation based on partial specialization:
#include<type_traits>
template<template<typename...> class, typename>
struct with_tmpl_args_of;
template<template<typename...> class OtherTmpl, template<typename...> class Tmpl, typename... Args>
struct with_tmpl_args_of<OtherTmpl, Tmpl<Args...>> {
using type = OtherTmpl<Args...>;
};
template<template<typename...> class OtherTmpl, typename T>
using with_tmpl_args_of_t = typename with_tmpl_args_of<OtherTmpl, T>::type;
// example
template<class ... Types> struct A {};
template<class ... Types> struct B {};
using A1_t = A<int, int, float>;
using B1_t = with_tmpl_args_of_t<B, A1_t>;
// test
static_assert(std::is_same_v<B1_t, B<int, int, float>>);
This is limited to class templates that do not use non-type template arguments. There is currently unfortunately no way to define template template parameters which accept both type and non-type template parameters in the same template template parameter's parameter.
Also beware of default arguments. This will not use OtherTmpl's default arguments, if one of Tmpl's default arguments matches that position in the template list and will fail if Tmpl's template list (including defaulted arguments) is larger than OtherTmpls.
Regarding the additional examples in your edit:
The second example works directly with the type transform I defined above:
template<typename T>
with_tmpl_args_of_t<B, T> func1()
{
}
The third one can be done like this:
template<typename A, typename... Ts>
with_tmpl_args_of_t<B, A> func2(Ts... args)
{
}
It guarantees that the return type has the same template arguments as A1_t, but it does accept all types as arguments, even if they don't match the types in the template arguments of A1_t. This should not usually be a problem. If the types are not convertible to the correct ones you will get an error at the point where you try the conversion.
If you must take the exact same types as in the template arguments of A1_t for function parameters, you can do something like (untested):
template<typename T>
struct func_with_tmpl_args_of;
template<template<typename...> class Tmpl, typename... Args>
struct func_with_tmpl_args_of<Tmpl<Args...>> {
template<typename F>
struct inner {
constexpr inner(F f) : f(std::move(f)) {}
constexpr static decltype(auto) operator()(Args... args) const {
return f(std::forward<Args>(args)...);
}
private:
F f;
};
};
// example
template<typename T>
constexpr inline auto func2 = func_with_tmpl_args_of<T>::inner{[](auto... args)
-> with_tmpl_args_of_t<B, T> {
// actual function body
}};
Here's a cut down example of what I'm trying to do:
#include <string>
#include <iostream>
#include <type_traits>
template <typename T>
class foo
{
public:
template <typename U>
typename std::enable_if<std::is_same<T, U>::value>::type
bar(const U& t)
{
std::cout << t << "\n";
}
};
template <typename... Args>
class baz
: public foo<Args>...
{
};
int main()
{
baz<double, std::string> b;
b.bar(1.0);
}
This gives me ambiguous function errors:
error: request for member 'bar' is ambiguous
b.bar(1.0);
note: candidates are: template<class U> typename std::enable_if<std::is_same<T, U>::value>::type foo<T>::bar(const U&) [with U = U; T = std::basic_string<char>]
note: template<class U> typename std::enable_if<std::is_same<T, U>::value>::type foo<T>::bar(const U&) [with U = U; T = double]
My questions are twofold:
Why is the inner template U not deduced? I'm supposing that it's due to ordering of template deduction and overload resolution, but can someone explain this?
Is there another way of going about what I'm trying to do?
I think the error message is misleading. The problem is actually name bar is available in multiple base classes and you've not used using directive to bring the names you want into the derived class scope.
Here is one working solution:
template <typename X, typename... Args>
class baz : public foo<X>, public baz<Args...>
{
public:
using foo<X>::bar; //bring the name from the first base
using baz<Args...>::bar; //bring the name from the second base
};
template <typename X>
class baz<X> : public foo<X> //specialization for one argument
{
//no using directive needed, as there is one base only!
};
Complete Demo
The problem has nothing to do with variadic templates, template argument deduction, or the like. It is that member functions of the same name from different base classes don't overload. Minimized example:
struct foo {
void f(int &);
};
struct bar {
void f(const int &);
};
struct foobar : foo, bar { };
int main(){
foobar fb;
int i;
fb.f(i); // clang complains: error: member 'f' found in multiple base classes of different types
}
Since in your code, foo<double> and foo<std::string> are distinct types, and lookup for bar finds a declaration in each, your code is ill-formed.
A possible fix is to write a baz::bar that explicitly dispatches to the appropriate foo::bar:
template <typename... Args>
class baz
: public foo<Args>...
{
public:
template <typename U>
void
bar(const U& t)
{
foo<U>::bar(t);
}
};
You can SFINAE baz::bar on U being one of the types in Args, if desired.
Another possible solution is to use the recursive implementation shown in Nawaz's answer.
Take the following class hierarchy:
template<typename T>
class Foo {
public:
T fooMethod() { ... }
};
class Moo : public Foo<bool> {
...
};
If I now somewhere write Moo::fooMethod the compiler will deduce Foo<bool>::fooMethod. How can I deduce Foo<bool> as parent of fooMethod myself before compile time?
Motivation: the compiler will not allow Foo<bool>::fooMethod to be passed as template parameter for bool (Moo::*)() since it will be of type bool (Foo<bool>::*)() in that context. But since I have multiple inheritance I dont know what parent fooMethod will be in, it must be deduced.
If I understand the problem correctly, it is possible to deduce the class a member function is defined in, using the following trait:
template<typename>
struct member_class_t;
template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...)> { using type = C; };
template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...) const> { using type = C const; };
// ...other qualifier specializations
template<typename M>
using member_class = typename member_class_t <M>::type;
after which you can write
member_class<decltype(&Moo::fooMethod)>
giving Foo<bool>.
To define member_class more generally, you should take into account volatile and ref-qualifiers as well, yielding a total of about 12 specializations. A complete definition is here.
Yes.
Let's say I have a simple variadic struct that holds a typedef:
template<typename... TArgs> struct TupleTypeHolder {
using TupleType = std::tuple<TArgs*...>;
};
I want to pass TupleTypeHolder<something> as a template parameter to another class, and get that typedef.
All of my tries do not compile.
// None of these is valid
template<template<typename...> class TTupleTypeHolder> struct TupleMaker {
using MyTupleType = TTupleTypeHolder::TupleType; // Not valid
using MyTupleType = typename TTupleTypeHolder::TupleType; // Not valid
};
template<template<typename... A> class TTupleTypeHolder> struct TupleMaker2 {
// A is not a valid name here
using MyTupleType = TTupleTypeHolder<A...>::TupleType; // Not valid
using MyTupleType = typename TTupleTypeHolder<A...>::TupleType; // Not valid
};
Is there a way to use the variadic template parameters (in this case, TupleTypeHolder's TArgs...) of a variadic template class from a class that uses the aforementioned class as a template variadic template parameter?
Usage example:
template<typename... TArgs> struct TupleTypeHolder {
using TupleType = std::tuple<TArgs*...>;
};
template<typename... TArgs> static int getSomeValue() { ... }
template<??? T1, ??? T2> class TupleMaker
{
std::pair<int, int> someValues;
using TupleType1 = T1::TupleType;
using TupleType2 = T2::TupleType;
TupleMaker() : someValues{getSomeValue<T1's TArgs...>(),
getSomeValue<T2's TArgs...>()} { }
};
class MyTupleMaker : TupleMaker<TupleTypeHolder<int, char>,
TupleTypeHolder<int, float>>
{ };
MyTupleMaker::TupleType1 tuple1{new int(1), new char('a')};
MyTupleMaker::TupleType2 tuple1{new int(35), new float(12.f)};
Working usage example:
#include <tuple>
template<typename... TArgs> struct TupleTypeHolder {
using TupleType = std::tuple<TArgs*...>;
};
template<typename... TArgs> static int getSomeValue() { return 42; }
// primary template:
template<class T1, class T2>
struct TupleMaker;
// partial specialization:
template<template<class...> class TT1, template<class...> class TT2,
class... T1, class... T2>
struct TupleMaker < TT1<T1...>, TT2<T2...> >
{
std::pair<int, int> someValues;
using TupleType1 = typename TT1<T1...>::TupleType;
using TupleType2 = typename TT2<T2...>::TupleType;
TupleMaker() : someValues{getSomeValue<T1...>(),
getSomeValue<T2...>()} { }
};
struct MyTupleMaker : TupleMaker<TupleTypeHolder<int, char>,
TupleTypeHolder<int, float>>
{ };
MyTupleMaker::TupleType1 tuple1{new int(1), new char('a')};
MyTupleMaker::TupleType2 tuple2{new int(35), new float(12.f)};
int main() {}
The primary template takes two types, as you're passing types. TupleTypeHolder<int, char> is a type, a specialization of a template, not a template itself. Template template-parameters however take templates as arguments (not types), such as:
template<template<class...> class Foo>
struct Bar
{
using type = Foo<int, double, char>;
};
Bar< std::tuple > b; // note: no template arguments for `std::tuple`!
With partial specialization, you can split a template specialization into the template and the parameters, that's how the above works.
Template-template parameters are not really type parameters, are parameters to specify a template. That means what you pass through a template-template parameter is not a type, is a template:
template<template<typename> class TPARAM>
struct give_me_a_template
{
using param = TPARAM; //Error TPARAM is not a type, is a template.
using param_bool = TPARAM<bool>; //OK, thats a type
};
As you can see, the first alias is invalid, because TPARAM is not a type, is a template. But the second is a type (Is an instance of the template).
That said, examine your problem: What you called TupleTypeHolder could be viewed as a variadic-template typelist. So your goal is to make tuples of the types specified with typelists, right?
You can use partial specialization to extract the content of a typelist:
template<typename TupleTypeHolder>
struct tuple_maker;
template<typename... Ts>
struct tuple_maker<TupleTypeHolder<Ts...>>
{
using tuple_type = std::tuple<Ts...>;
};
An example of its usage could be:
using my_types = TupleTypeHolder<int,int,int>;
using my_tuple_type = typename tuple_maker<my_types>::tuple_type;
Of course this is not the exactly solution to your implementation, you need to extend the concept to multiple typelists (As your question showed). What I have provided is the guide to understand the problem and its solution.