Using templates to set whether class members are const

Viewed 233

I have a templated class and I'd like to almost toggle whether things within it are const based on the template type.

pseudocode:

template<bool isConst>
Class ConstableClass
{
public:
    // if isConst == true make this method const
    void DoSomething() "const";

    // if isConst == true make the returned smartpointer type const method const
    std::unique_ptr<"const" int> operator->() "const";

private:
    // if isConst == true add const at the start of the next line
    "const" int foo;
}

Is this sort of thing possible?

4 Answers

With type-traits and SFINAE:

template <bool isConst>
class ConstableClass
{
public:
    template <bool C = isConst, typename = std::enable_if_t<C>>
    void DoSomething() const;

    template <bool C = isConst, typename = std::enable_if_t<!C>>
    void DoSomething();

    template <bool C = isConst, typename = std::enable_if_t<C>>
    std::unique_ptr<const int> operator->();

    template <bool C = isConst, typename = std::enable_if_t<!C>>
    std::unique_ptr<int> operator->();

private:
    std::conditional_t<isConst, const int, int> foo{};
};

With a specialization:

template <bool isConst>
class ConstableClass
{
public:
    void DoSomething() const;

    std::unique_ptr<const int> operator->() const;

protected:
    const int foo{};
};

template <>
class ConstableClass<false>
{
public:
    void DoSomething();

    std::unique_ptr<int> operator->();

protected:
    int foo{};
};

With constraints in :

template <bool isConst>
class ConstableClass
{
public:
    void DoSomething() const requires isConst;

    void DoSomething() requires not isConst;

    std::unique_ptr<const int> operator->() const requires isConst;

    std::unique_ptr<int> operator->() requires not isConst;

private:
    std::conditional_t<isConst, const int, int> foo{};
};

The first use case is not possible. You can't make function modifiers conditional and based on some template. However, I imagine you are trying to do this because you don't want to copy&paste code between the const and regular version of a function. In that case, just write a private impl method which does the actual work and use it in the cost and non-const version of the class.

private:
int& get_impl() const {...}
public:
const int& get() const {return get_impl();}
int& get() {return get_impl();}

The rest is possible and quite simple:

std::unique_ptr<"const" int>
// we can do this by:
std::unique_ptr<std::conditional_t<isConst, const int, int>> ...

// this can be written more elegantly as
template <typename T, bool isConst>
using const_if_t = std::conditional_t<isConst, const T, T>;

std::unique_ptr<const_if_t<int, isConst>>;

Making member variables const conditionally would be pretty pointless because this already happens when you make a variable const anyways.

No, it is not possible the way you want to do it. While there is a way to manipulate member variable types and function return types, there is no way to add/remove const specifier to a method. This means you have to write 2 separate specializations of the class definition for the true and false values of the template parameter.

I have a templated class and I'd like to almost toggle whether things within it are const based on the template type. Is this sort of thing possible?

Yes, and I find specializations to be more readable (unless you have access to concepts). The example below shows, rather explicitly, the two cases.

template<bool isConst>
class ConstableClass;

template<>
class ConstableClass<false>
{
  int foo;
public:
  void DoSomething();
  std::unique_ptr<int> operator->();
};

template<>
class ConstableClass<true>
{
  int const foo;
public:
  ConstableClass()
    : foo{0} {} // example
  void DoSomething() const;
  std::unique_ptr<int const> operator->() const;
};

Keep in mind that you do need to add a constructor for the true case, as adding const to foo implicitly deletes it.

Related