0

In C++, I have a base class parameterized over 1 type, and for each subclass having a specific additional structure, I want to map that subclass to another type, which is also a subclass of the same base. However, my code generates an error, the compiler will not do what I intend to do. So

  1. How should I fix my code to get int main() working?
  2. Can the template function wrap be written into Base<A>* wrap(Sub& ), returning Wrap<A, Sub> if the argument has a method Sub::g(A& ), or just Sub if not (the identity)?

.

using uint = unsigned int;

// for each type A, there is a base class Base<A>
template <typename A>
class Base
{
public:
    virtual void f(A& ) = 0;

};


// for each type A, and for each type Sub having methods 
// Sub::f(A& ) and Sub::g(A& ), there is a special subclass 
// of Base<A>, with the name Wrap<A, Sub>.
//
// NOTE: the type Sub has more structure, it is actually
//       a subclass of Base<A>.
template <typename A, typename Sub>
class Wrap : public Base<A>
{
friend Wrap<A, Sub>* wrap(Sub& sub);

public:
    virtual void f(A& a) override 
    {
        // ... do stuff on 'a', using Sub::f() and Sub::g() ...
    }

private:
    Wrap(Sub& s) : sub_( s ) { }
    Sub& sub_;


};


// for each type A, and for each type Sub having methods
// Sub::f(A& ) and Sub::g(A& ), map this to Wrap<A, Sub>
template <typename A, typename Sub>
Wrap<A, Sub>* wrap(Sub& sub)
{
    return new Wrap<A, Sub>( sub );
}



// a subclass of Base<uint>, with the additional 
// structure of having a method Subclass::g(uint& )
class Subclass : public Base<uint>
{
public:
    virtual void f(uint& a) override { /*...*/ }

    void g(uint& a) { /*...*/ }
};



int main()
{
    Subclass sub;

    Base<uint>* w = wrap( sub );
    // ^ no matching function for call to ‘wrap(Subclass&)’
    //   template argument deduction/substitution failed:
    //   couldn't deduce template parameter ‘A’

    uint a = 0;
    w->f( a );

    // ... delete w ...
    return 0;
}
telephone
  • 1,131
  • 1
  • 10
  • 29

2 Answers2

1

Something along these lines perhaps:

template <typename A>
class Base
{
public:
    typedef A MyType;
    virtual void f(A& ) = 0;
};

template <typename Sub>
Wrap<typename Sub::MyType, Sub>* wrap(Sub& sub)
{
    return new Wrap<typename Sub::MyType, Sub>( sub );
}
Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • If so, should `MyType` instead be named `value_type`, as in std? – telephone Jul 09 '14 at 19:29
  • 1
    How should I know? You are the one who knows what the type parameter is actually used for. If `value_type` is a meaningful name for it, then by all means make it so. – Igor Tandetnik Jul 09 '14 at 19:43
  • I thought `value_type` might be the common, "standarized" name for the type of a parameterized class, something like `begin()`/`end()` is the common way for containers. That naming the type `value_type` could give my class more possibilities. I am not a experienced C++ user, but has OK language knowledge. – telephone Jul 10 '14 at 13:11
  • 1
    `value_type` is a common name for a type of values stored in a container. It's not a common name for every template type parameter under the sun (for one thing, standard containers take more than one template parameter, only one of which could possibly become `value_type`). Is `Base` a container, and `A` a type of values stored in it? – Igor Tandetnik Jul 10 '14 at 13:45
  • No, `Base` is not a container. My own descriptive name will be better too. Thank your for your clarification of `value_type`. – telephone Jul 10 '14 at 16:33
  • However, is it possible to have 2 instances of this `wrap` function, if the return value is changed to `Base*`? The general version is the identity (well, pointer to the argument), and the other is specialized for arguments having the `g()` method. – telephone Jul 10 '14 at 16:36
  • Thank you for both your answer and link to SFINAE, shall look at that myself. In my code above, I had to change the declaration of my friend function, in class `Wrap`, into a templated version parameterized by 1 type, for the `Sub` argument. Why was this necessary? Since, for each instance of `Wrap`, there is only one friend function. – telephone Jul 10 '14 at 17:05
  • 1
    As written, the function you declare a friend is not the same function that you actually implement. Each instantiation of `Wrap` declares and befriends a particular overload of a non-template function named `wrap` (that you never define). In addition, and independently, you have defined a function template named `wrap`, which is not a friend of `Wrap`. `friend` declaration is weird this way. – Igor Tandetnik Jul 10 '14 at 17:16
1

Two problems:

1) In Base<uint>* w = wrap(sub); type A cannot be deduced, so you have to provide it explicitly:

Base<uint>* w = wrap<uint>(sub);

2) You need to properly define a template function as friend of a templated class:

template <typename A, typename Sub>
class Wrap : public Base<A>
{
template <typename T1, typename T2>
friend Wrap<T1, T2>* wrap(T2& sub);
...
}

Live Demo

101010
  • 41,839
  • 11
  • 94
  • 168
  • 1. I don't see the other possibilities for the type A, like the compiler does... But I wanted to change the coding for `class Wrap`/`wrap()` in order that line in `main()` working. – telephone Jul 10 '14 at 13:16
  • 2. Yes, my compiler warned me about something like that. However, for each type-applied instance of `Wrap`, there is only 1 friend function. Doing like your code seems to create a friend function for any two types `T1` and `T2`, which is not what I need. Is my assumption correct, that your template friend function creates multiple friend functions? – telephone Jul 10 '14 at 13:20
  • Got answer for my question in the 2nd comment, from Igor. Thanks for your answer :) – telephone Jul 10 '14 at 17:27