6

Is there a way to compile a method or not, depending on template argument ? I'm trying to create a Coordinate class that can handle 2, 3 or more dimensions. I want to provide acces methods as x(), y() and z(), but I would like z() method to be accessible only if dimension is larger than 3. For now (as you can see below), I use a static_assert to prevent use of z() for coordinates of dimension 2.

template<typename DataType, int Dimension>
class Coord
{
private:
    std::array<DataType, Dimension> _data;

public:

    // how to achieve some kind of compile_if()
    DataType& z()
    {
        static_assert(Dimension >= 3, "Trying to access an undefined dimension.");
        return _data[2];
    }
};

What I would like to do is hide existence of z() for dimension 2, so that this

Coord<int, 2> ci2(0,0);
ci2.z() = 3;   // shouldn't compile

doesn't compile without the use of static_assert. I've seen many question around std::enable_if, but for what I understand it is use to enable or disable specific overloads.

Question is : is there a way to make a method available or not depending on a compile-time argument ?

undu
  • 2,411
  • 3
  • 21
  • 28

3 Answers3

8

For example, you can declare your function as template and use std::enable_if like this

template<typename DataType, int Dimension>
class Coord
{
private:
    std::array<DataType, Dimension> _data;

public:
    template <class T = DataType>
    typename std::enable_if<Dimension >= 3, T&>::type
    z()
    {
        return _data[2];
    }
};
awesoon
  • 32,469
  • 11
  • 74
  • 99
6

You can use specialization for this:

template<typename DataType, int Dimension, bool HaveZ = (Dimension >= 3)>
class Coord;

template<typename DataType, int Dimension>
class Coord<DataType, Dimension, false>
{
private:
    std::array<DataType, Dimension> _data;

public:
};

template<typename DataType, int Dimension>
class Coord<DataType, Dimension, true>
{
private:
    std::array<DataType, Dimension> _data;

public:

    DataType& z()
    {
        return _data[2];
    }
};

You can lift the shared members out into a separate struct to prevent code duplication.

Pubby
  • 51,882
  • 13
  • 139
  • 180
  • Shared members could be in a base class, then `Coord` should inherit from this base class. – JSQuareD Mar 18 '13 at 16:34
  • 1
    I must admit I am confused as to why `std::enable_if` is not applicable here, clang also accepts it and behaves as I expected. – hmjd Mar 18 '13 at 16:40
  • @hmjd this answer seems to explains it: http://stackoverflow.com/questions/6972368/stdenable-if-to-conditionally-compile-a-member-function?lq=1 (I'm guessing GCC is correct here, but I could be wrong!) – Pubby Mar 18 '13 at 16:42
3

You could use this approach based on specialization specialization:

#include <array>

template<typename DataType, int Dimension, bool = (Dimension < 3)>
class Coord
{
protected:
    std::array<DataType, Dimension> _data;
};

template<typename DataType, int Dimension>
class Coord<DataType, Dimension, false> : public Coord<DataType, Dimension, true>
{
public:
    DataType& z()
    {
        return this->_data[2];
    }
};

int main()
{
    Coord<double, 3> c3;
    c3.z(); // OK

    Coord<double, 2> c2;
    c2.z(); // ERROR!
}
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451