Thomas
Thomas

Reputation: 333

member variable and constructor dependent on template parameter

In C++11, I'd like to have a member variable in a class and a constructor for its initialization only if its default template value was chosen (only for supported types like int, of course).

What are recommended ways to achieve this (boost allowed)?

Something like:

template< int _x = -1 > struct C {
    C() {} // only available if _x != -1
    C( int x ) : x( x ) {} // only available if _x == -1
    // more methods that are common for all _x and refer to _x / x
    private:
    int x; // only available if _x == -1
    // more members that are common for all _x
};

Or, put in another way: For size and speed optimization, I would like to use a compile time constant instead of a value stored in a member variable if another value than the template default was chosen.

--

Here is an example to make everything clearer:

template< int _size = -1 > struct Block {
    Block() { buf = mmap( _size, ... ); } // exists only when size!=-1
    Block( int s ) { buf = mmap( size = s, ... ); } // exists only when size==-1
    ~Block() { munmap( buf, getSize() ); } // should use the correct size
    int getSize() const { return ???; } // gets _size if !=-1, size otherwise
    // other methods that use buf and getSize()
    private:
    void *buf;
    const int size; // only exists for size == -1!
};

This solves it partially:

template< int _x > struct X {
    int getX() const { return _x; }
};
template<> struct X< -1 > {
    X( x ) : x( x ) {}
    int getX() const { return _x; }
    private:
    int x;
};

template< int _x = -1 > struct C : X< _x > {
    C() {} // only available if _x != -1
    C( int x ) : X< _x >( x ) {} // only available if _x == -1
    // more methods that are common for all _x and use this->getX()
};

But what about the constructors of C, and are other / nicer solutions available?

Upvotes: 5

Views: 2291

Answers (3)

user2218982
user2218982

Reputation:

I'm with Kerrek SB on this one. Put your common code, namely the runtime buffer handling in a common base class an create two derived classes, one for your statically sized buffer class and one for your dynamic buffer class. Or better yet, according to common coding guidelines, use composition.

    class buffer_impl {
    public:
        buffer_impl(int size) : data_ {mmap( size, ... )}, size_ {size} {}
        ~buffer_impl() { munmap( data_, getSize() ); }
        int getSize() const noexcept { return size_; }

        // other buffer routines
        // ...
    private:
        void* data_;
        int size_;
    };

    template <int _size = -1 >
    class buffer {  // static size
    public:
        buffer() : impl_ {_size} {}
        static constexpr int getSize() noexcept { return _size; }
    private:
        buffer_impl impl_;
    };

    template <>
    class buffer<-1> {  // dynamic size
    public:
        buffer(int size) : impl_ {size} {}
        int getSize() const noexcept { return impl_.getSize(); }
    private:
        buffer_impl impl_;
    };

Upvotes: 0

Daniel Frey
Daniel Frey

Reputation: 56873

Just an idea, but maybe it helps: You could try to use a base class only for the minimal differences and "fake" the member variable for when it's not there to allow the rest to compile:

template< int _x > class B
{
public:
  B() {}
protected:
  static const int x = _x;
};

template<> class B< -1 >
{
public:
  B( int i ) : x( i ) {}
protected:
  int x;
};

template< int _x = -1 >
class C : public B<_x>
{
public:
  using B<_x>::B; // inherit B's ctors

  void f()
  {
    if ( x == ... ) // uses either the member variable x or the static const int x!
  }
};

but as I said, it's just an idea...

Upvotes: 5

Kerrek SB
Kerrek SB

Reputation: 477238

Specialization is the way to go:

template <int N> struct C
{
    C(int n) : n_(n) { }
    int n;
};

template <> struct C<-1>
{
    C() { }
    C(int n) : n_(n) { }
    int n;
};

Upvotes: 2

Related Questions