1

When you defined a template in C++ (with type parameter) you can pass as type actually a pointer to a type, for example:

MyClass<Foo*>... // edited

I wonder if this is really used? Because such question is too broad, let's focus on core C++, meaning -- is it used in STL or Boost? If yes (in STL/Boost) for what purpose?

Please note that I am asking about passing pointer as an argument (from OUTSIDE). Using pointer to passed argument INSIDE template is another story, and I don't ask about that.

Update

The difference between passing a pointer and using a pointer.

Take a look at those posts: How is vector implemented in C++ and passing pointer type to template argument .

myname<char*>(...

myname is a function. The type which is passed to template (template, not a function) is pointer to char.

Now, using a pointer inside:

template <class T...
class vector {
private:
  T*                    data_;

You pass an int (for example) but nothing stops the template of using a pointer to it.

I am interested in the first, not the second, case.

Community
  • 1
  • 1
greenoldman
  • 16,895
  • 26
  • 119
  • 185
  • 5
    Yes, sometimes people store pointers in containers for example. But note your syntax is wrong. – juanchopanza Mar 17 '14 at 17:45
  • Could you formulate your question clearly? What do you mean when you say 'to pass a pointer as an argument from outside/inside'? – Constructor Mar 17 '14 at 18:02
  • @juanchopanza, you are right. Thank you for your answer, but it is obvious that there is (almost) always evidence for some behaviour. I ask solely about STL/Boost. – greenoldman Mar 17 '14 at 18:31
  • Mmm. Was gonna post my answer here. Apparently, I'm too late. Well, here's a link to an old use case I remembered http://ideone.com/G9Kzfy (2012). Nothing I particularly recommend, but it has it's uses (/cc @ThePhD) – sehe Mar 17 '14 at 23:38
  • `std::iterator_traits`? – Mooing Duck Mar 17 '14 at 23:49
  • @MooingDuck Mmmm. It seems I may have misread the question as something that is actually interesting instead of trivial. On re-reading I noticed that indeed, the question boils down to "Why is pointer type also a type"... Huh – sehe Mar 18 '14 at 07:24
  • I had as well until I got to the conments, and reread. – Mooing Duck Mar 18 '14 at 15:10

3 Answers3

2

Like I commented, I remembered a use case where functions would be registered as event callbacks by their actual address.

This way the trampoline functions to invoke member functions would be generated statically for each member function registered.

I'm not sure this is something I'd actually have a use for, but it does demonstrate a (contrived?) way in which pointer-to-function template arguments can be used.

#include <iostream>
#include <vector>

const static auto null = nullptr;

template<typename TFuncSignature>
class Callback;

template<typename R, typename A1>
class Callback<R (A1)> {
public:
    typedef R (*TFunc)(void*, A1);

        Callback() : obj(0), func(0) {}
        Callback(void* o, TFunc f) : obj(o), func(f) {}

        R operator()(A1 a1) const {
                return (*func)(obj, a1);
        }

        typedef void* Callback::*SafeBoolType;
        operator SafeBoolType () const {
                return func != 0? &Callback::obj : 0;
        }

        bool operator! () const {
                return func == 0;
        }

        bool operator== ( const Callback<R (A1)>& right ) const {
                return obj == right.obj && func == right.func;
        }

        bool operator!= ( const Callback<R (A1)>& right ) const {
                return obj != right.obj || func != right.func;
        }

private:
        void* obj;
        TFunc func;
};

template<typename R, class T, typename A1>
struct DeduceMemCallbackTag {
        template<R (T::*Func)(A1)>
        static R Wrapper(void* o, A1 a1) {
                return (static_cast<T*>(o)->*Func)(a1);
        }

        template<R (T::*Func)(A1)>
        inline static Callback<R (A1)> Bind(T* o) {
                return Callback<R (A1)>(o, &DeduceMemCallbackTag::Wrapper<Func>);
        }
};

template<typename R, typename A1>
struct DeduceStaticCallbackTag {
        template<R (*Func)(A1)>
        static R Wrapper(void*, A1 a1) {
                return (*Func)(a1);
        }

        template<R (*Func)(A1)>
        inline static Callback<R (A1)> Bind( ) {
                return Callback<R (A1)>( 0, &DeduceStaticCallbackTag::Wrapper<Func> );
        }
};

template<typename R, class T, typename A1>
DeduceMemCallbackTag<R, T, A1> DeduceMemCallback(R (T::*)(A1)) {
        return DeduceMemCallbackTag<R, T, A1>();
}

template<typename R, typename A1>
DeduceStaticCallbackTag<R, A1> DeduceStaticCallback(R (*)(A1)) {
        return DeduceStaticCallbackTag<R, A1>();
}

template <typename T1> class Event {
public:
        typedef void(* TSignature)( T1 );
        typedef Callback<void( T1 )> TCallback;

protected:
        std::vector<TCallback> invocations;
        std::vector<Event<T1>*> events;

public:
        const static int ExpectedFunctorCount = 2;

        Event () : invocations(), events() {
                invocations.reserve( ExpectedFunctorCount );
                events.reserve( ExpectedFunctorCount );
        }

        template <void (* TFunc)(T1)> void Add (  ) {
                TCallback c = DeduceStaticCallback( TFunc ).template Bind< TFunc >( );
                invocations.push_back( c );
        }

        template <typename T, void (T::* TFunc)(T1)> void Add ( T& object ) {
                Add<T, TFunc>( &object );
        }

        template <typename T, void (T::* TFunc)(T1)> void Add ( T* object ) {
                TCallback c = DeduceMemCallback( TFunc ).template Bind< TFunc >( object );
                invocations.push_back( c );
        }

        void Invoke ( T1 t1 ) {
                size_t i;
                for ( i = 0; i < invocations.size(); ++i ) {
                        invocations[i]( t1 );
                }
                for ( i = 0; i < events.size(); ++i ) {
                        (*events[i])( t1 );
                }
        }

        void operator() ( T1 t1 ) {
                Invoke( t1 );
        }

        size_t InvocationCount ( ) {
                return events.size( ) + invocations.size( );
        }

        template <void (* TFunc)(T1)> bool Remove ( ) {
                TCallback target = DeduceStaticCallback( TFunc ).template Bind< TFunc >( );
                for ( size_t i = 0; i < invocations.size(); ++i ) {
                        TCallback& inv = invocations[i];
                        if ( target == inv ) {
                                invocations.erase( invocations.begin() + i );
                                return true;
                        }
                }
                return false;
        }

        template <typename T, void (T::* TFunc)(T1)> bool Remove ( T& object ) {
                return Remove<T, TFunc>( &object );
        }

        template <typename T, void (T::* TFunc)(T1)> bool Remove ( T* object ) {
                TCallback target = DeduceMemCallback( TFunc ).template Bind< TFunc >( object );
                for ( size_t i = 0; i < invocations.size(); ++i ) {
                        TCallback& inv = invocations[i];
                        if ( target == inv ) {
                                invocations.erase( invocations.begin() + i );
                                return true;
                        }
                }
                return false;
        }

};

namespace IntStatic {
    void VoidTest    ()         { std::cout << "INTO THE VOID"         << std::endl; }
    void IntTest     (int num)  { std::cout << "Got myself a "         << num  << " !"    << std::endl; }
    void IntTest2    (int num)  { std::cout << "Now _I_ Got myself a " << num  << " !"    << std::endl; }
}
struct Int {
    void Test        (int num)  { std::cout << num  << " on the inside of a class... ?"   << std::endl; }
    void Test2       (int num)  { std::cout << num  << " on the inside of a struct, yo !" << std::endl; }
    static void Test3(int snum) { std::cout << snum << " on the inside of a class... ?"   << std::endl; }
};

int main(int argc, char* argv[]) {
    Event<int> intev;
    Int i;
    intev.Add<Int, &Int::Test>(i);
    intev.Add<&IntStatic::IntTest>();
    intev.Add<&IntStatic::IntTest2>();
    //intev.Add( Int::Test3 );
    intev(20);
    intev.Remove<&IntStatic::IntTest>();
    intev.Remove<&IntStatic::IntTest>();
    intev.Remove<Int, &Int::Test>(i);
    //intev.Remove( Int::Test3 );
    //intev.Remove( i, &Int::Test );
    intev(20);
    return 0;
}

The actual code is famously written by @ThePhD, credits to him. See it Live on Coliru

sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    The code really sells a statement. I'm thoroughly impressed with the solution. Reminds me of Duff's device. – Captain Giraffe Mar 17 '14 at 23:59
  • Thank you, I am not being lazy, simply the code a little overwhelms me -- where is the instantiation with pointers to the type? Once again, clarification, `vector` expects any type, right? So when you build `vector` with `std::vector`, that's the instantiation with pointers (but `vector` does not care, for it is just a type as any other). For example `Add` does not qualify because it defines template that way that it **expects** pointer. – greenoldman Mar 18 '14 at 13:32
  • 1
    @greenoldman I [don't think](http://stackoverflow.com/questions/22461478/is-ability-to-instantiate-template-with-pointer-types-really-used/22467720?noredirect=1#comment34184192_22461478) I've understood your question. Sorry about that. Lemme retry, **[is this what you were after](http://stackoverflow.com/a/22481666/85371)**? – sehe Mar 18 '14 at 14:01
  • @sehe, yes, very close. You see this `report` instantiations ` report()` or `optional` -- `optional`. Those classes expected ANY type, the instantiations are using pointers. So is there a case in STL/Boost, that you have a template (optional, vector, etc) that expects any type, and the instantiation comes with the pointer? I know I can do it, but is there evidence of such usage within Boost/STL? – greenoldman Mar 18 '14 at 14:08
  • @greenoldman sadly, I appear to be not so good with understanding English as I'm with C++. I don't what it means when you say "the instantiation comes with the pointer". Sorry. – sehe Mar 18 '14 at 14:24
2

A pointer is just a type, so anywhere you can use some_template<T> you might also want to use some_template<T*>

The parts of the standard library based in the STL use std::iterator_traits<Iter> in many places, and Iter might be a pointer type.

Some implementations of std::unique_ptr<T, D> use a data member of type std::tuple<T*, D> (e.g. this is true for GCC's implementation, but this is an implementation detail that you should not care about).

Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • You misread me. I am not asking if this is technically possible (I know it is), I asked if this was used in STL or Boost. – greenoldman Mar 18 '14 at 13:26
  • 1
    The STL and Boost *define* templates for _you_ to use, why do you care how they use them internally? This is one of the weirdest SO questions I've seen. The STL uses `iterator_traits` internally, and sometimes `T` is a pointer type, happy? – Jonathan Wakely Mar 18 '14 at 14:17
  • Yes, this is the answer to my question. I hope it was honest answer and not a joke, because I sense some sarcasm :-) (OT: is there a badge for being the weirdest?). – greenoldman Mar 18 '14 at 14:19
  • Not sarcasm, but I'm wondering WTF the question is about. Anyway I've updated my answer with two examples – Jonathan Wakely Mar 18 '14 at 14:19
  • It is pretty simple, Boost and STL together are quite a big libraries, if there was no such usage I could (at least initially) assume there is no **real** need for such usage and optimize the templates to forbid such usage. In my own language which I develop, why invent, when you can steal the ideas? :-) – greenoldman Mar 18 '14 at 14:21
  • Using pointers as template arguments is perfectly valid, there is no reason to prevent it, and how would preventing it be an optimisation? – Jonathan Wakely Mar 18 '14 at 14:25
  • My original answer showed things like `std::vector`, not just as something "technically possible" but something used in the real world. – Jonathan Wakely Mar 18 '14 at 14:27
  • In such case sorry, I understood you in sense "here, it is possible", not "here, this is typical case commonly **used**". – greenoldman Mar 18 '14 at 14:29
2

You could have different semantics by specialization, e.g.:

template <typename T> struct referred_sizeof  { 
    static constexpr size_t value = sizeof(T);
};

Now, this could be specialized:

template <typename T> struct referred_sizeof<T*> { 
    static constexpr size_t value = sizeof(T);
};

template <typename T> struct referred_sizeof <boost::optional<T*> > { 
    static constexpr size_t value = sizeof(boost::optional<T*>) + sizeof(T);
};

Which makes the behaviour:

static_assert(referred_sizeof <int>::value == referred_sizeof <int*>::value, "passes");

This application is what others referred to in comments as implementing traits classes.

Full sample, adding specialization for boost::tuple<...> just for fun: See it Live On Coliru

int main()
{
    report<double>();
    report<double *>();
    report<boost::optional<double> >();
    report<boost::optional<double> *>();
    report<boost::optional<double *> *>();

    report<boost::tuple<boost::optional<double *> *, double> >();
}

Prints

void report() [with T = double]: referred_sizeof is 8
void report() [with T = double*]: referred_sizeof is 8
void report() [with T = boost::optional<double>]: referred_sizeof is 16
void report() [with T = boost::optional<double>*]: referred_sizeof is 16
void report() [with T = boost::optional<double*>*]: referred_sizeof is 24
void report() [with T = boost::tuples::tuple<boost::optional<double*>*, double>]: referred_sizeof is 40

Full implementation for reference

#include <iostream>
#include <boost/optional.hpp>
#include <boost/tuple/tuple.hpp>

template <typename... Ts> struct referred_sizeof;

// base cases
template <typename T> struct referred_sizeof<T> { 
    static constexpr size_t value = sizeof(T);
};

template <typename T> struct referred_sizeof<T*> { 
    static constexpr size_t value = referred_sizeof<T>::value;
};

template <typename T> struct referred_sizeof<boost::optional<T*> > { 
    static constexpr size_t value = sizeof(boost::optional<T*>) + referred_sizeof<T>::value;
};

template <typename... Ts> struct referred_sizeof<boost::tuple<Ts...> > { 
    static constexpr size_t value = referred_sizeof<Ts...>::value; // TODO take into account padding/alignment overhead?
};

static_assert(referred_sizeof<int>::value == referred_sizeof<int*>::value, "passes");

template <typename T1, typename... Ts> struct referred_sizeof<T1, Ts...> {
    static constexpr size_t value = referred_sizeof<T1>::value + referred_sizeof<Ts...>::value;
};

template <typename T>
void report()
{
    std::cout << __PRETTY_FUNCTION__ << ": referred_sizeof is " << referred_sizeof<T>::value << "\n";
}
sehe
  • 374,641
  • 47
  • 450
  • 633