0

I'm using inheritance with a set of classes. One of the child classes takes in an std::function(ReturnTy<ParamTypes...>), along with the ParamTypes arguments. The class signature and constructor look like:

template<class ReturnTy, class... ParamTypes> class Child : public Interface { public: Child(ReturnTy default_value, ParamTypes... args) : func_set_(false) , m_Args(std::make_tuple(std::forward<ParamTypes>(args)...)) , m_ReturnValue(default_value) {} private: bool func_set_; std::function<ReturnTy(ParamTypes...)> m_Funciton; std::tuple<ParamTypes...> m_Args; ReturnTy m_ReturnValue; }; 

My issue is when I want to specialize for the case where there are no parameters. Furthermore, I also want to specialize for the case which ReturnTy=void and there are parameters. I found an answer that is close to what I'm looking for here, but it doesn't exactly cover what I'm trying to do because that question uses compile-time integers as template parameters, where I'm using types. It also concerns functions instead of classes. I feel like I'm close, but I just need some help to make my code work.

For reference, here is what I have for the other specializations (shortened):

template<class ReturnTy> class Child<ReturnTy> : public Interface { public: Child(ReturnTy default_value) : // The same as first class without m_Args {} private: // Same as first class without m_Args }; template<class... ParamTypes> class Child<void, ParamTypes...> : public Interface { public: Child(ParamTypes... args) : // Same as first class without m_ReturnValue private: // Same as first class without m_ReturnValue }; 



Edit
Specifically, the issue comes from something like the following line of code:

Child<void> obj1(5); 
4
  • Just so everyone is aware, I've omitted methods from the class that are not relevant to my question. For example, there is a set_function() method that will initialize the std::function, and there is a virtual run() function from the interface that does m_ReturnValue = std::apply(m_Function, m_Args). Commented Feb 6, 2020 at 22:03
  • In case of Child<void>, which specialization should be selected? The one with ReturnTy only or the one with void in first place? Commented Feb 6, 2020 at 22:12
  • @max66 I want the one with just void to be selected, but I'm not sure how to tell the compiler to select that one. Commented Feb 6, 2020 at 22:15
  • Sorry: not clear for me: do you mean the last one: class Child<void, ParamTypes...>? Commented Feb 6, 2020 at 22:17

2 Answers 2

1

The problem is that your specializations are of the same level (no one is more specialized that the other) and Child<void> matches both.

If you want that Child<void> matches the Child<ReturnTy> case (otherwise the solution is simple and elegant: in the second specialization, split the ParamTypes... list in a Par0 mandatory type and the rest of the ParamTypes...) I don't see a simple and elegant solution.

The best I can imagine, at the moment, is add a level of indirection (add a Child_base class) adding also a template parameter to explicit the desired solution.

Maybe can be made in a simpler way (sorry but, in this moment, I can try with a compiler) but I imagine something as follows

template <typename RT, bool, typename ... PTs> class Child_base : public Interface { // general case (no empy PTs... list and no void return type) }; template <typename ... PTs> class Child_base<void, true, PTs...> : public Interface { // case return type is void (also empy PTs... list) }; template <typename RT> class Child_base<RT, false> : public Interface { // case return type only, but not void, and empy PTs }; template <typename RT, typename ... PTs> class Child : public Child_base<RT, std::is_same_v<void, RT>, PTs...> { }; 

This way, Child<void> inherit from Child_base<void, true> that matches the first specialization of Child_base but doesn't match the second one.

I propose another way about Child: instead of define it as a class derived from Child_base, you can try defining it as a using alias of Child_base

template <typename RT, typename ... PTs> using Child = Child_base<RT, std::is_same_v<void, RT>, PTs...>; 

Maybe renaming Child_base with a more appropriate name.

Sign up to request clarification or add additional context in comments.

8 Comments

Sorry for the stupid question, but will I need to still specify a constructor for Child, or will the one from Child_base be used by default?
@DrakeJohnson - Not a stupid question at all, IMHO: it's a little complicated. I'm not an expert but, as far I know, if for "used" do you mean that the derived class can explicitly call a base class constructor (if accessible), to initialize the base class, yes: it can. And a default base class constructor can default initialize the base component in the derived class. But if for "used" do you mean that a base class can be inherited from the derived class and used as a constructor for the derived class, the answer is: no by default, yes using using (but with using only from C++11)(continue
@DrakeJohnson - anyway, I suppose that is more useful if you read the "Inheriting Constructor" section in the cpp reference using declaration page
@DrakeJohnson - anyway, I've improved my answer adding an example of use of using that should completely wipe away the inheritance problem
@DrakeJohnson - sorry: in my first comment, " But if for "used" do you mean that a base class can be inherited from the derived class and used as a constructor for the derived class" is wrong: it should be " But if for "used" do you mean that a base class constructor can be inherited from the derived class and used as a constructor for the derived class"
|
0

issue is that Child<void> matches 2 (partial) specializations (where none are more specialized than the other):

  • template<class ReturnTy> class Child<ReturnTy> with [ReturnTy = void]
  • template<class... ParamTypes> class Child<void, ParamTypes...> with empty pack.

You so need extra specialization:

template<> class Child<void> : public Interface { public: Child() = default; // .... private: std::function<void()> m_Function; }; 

Demo

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.