Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a method const based on the template parameter?

Tags:

c++

constants

I have a class with a template parameter, and I want to call a method of it. It looks something like this:

template <typename T>
class Foo {
public:
    void doSomething() {
        for (auto& t: ts) {
            t.doSomething();
        }
    }
private:
    std::vector<T> ts;
};

This works, but I want to make doSomething() const if T itself is const (it is assumed that T::doSomething() will be const too). I found a possible solution (based on this question), but I don't like it.

template <bool enabled = std::is_const<T>::value>
typename std::enable_if<enabled, void>::type
doSomething() const {
    for (auto& t: ts) {
        t.doSomething();
    }
}

template <bool enabled = !std::is_const<T>::value>
typename std::enable_if<enabled, void>::type
doSomething() {
    for (auto& t: ts) {
        t.doSomething();
    }
}

It works fine, but it has a code duplication. Is there any way to avoid it?

like image 213
petersohn Avatar asked May 19 '15 19:05

petersohn


1 Answers

While not perfect here is a workaround: we have a non const member _doSomething() where we have the code that is the same for const and non const, except the function called on the underlying object. Since this member is non const we have to const_cast this to call it from a const Foo.

Since the code inside _doSomething is const safe, it is safe to (const_)cast const away.

Your code also will not compile for const, since you can not have a vector of const. A vector's elements must be assignable, which const types are typically not (they really should not, however: https://stackoverflow.com/a/17313104/258418).
You might want to consider std::vector<T*> rather than std::vector<T>. (I.e. store pointers rather than the objects in the vector)

#include <iostream>
#include <vector>

using namespace std;

class Bar {
public:
    Bar() {}
    void doSomething() const {
        std::cout << "const" << endl;
    }

    void doSomething() {
        std::cout << "NON const" << endl;
    }
};


template <typename T>
class Foo {
    void _doSomething() {
        /*for (auto& t: ts) {
            t.doSomething();
        }*/
        test.doSomething();
    }
public:
    Foo()/*T element) : ts({element})*/ {}

    template <bool enabled = std::is_const<T>::value>
    typename std::enable_if<enabled, void>::type
    doSomething() const {
        const_cast<typename std::remove_const<Foo<T>>::type*>(this)->_doSomething();
    }

    template <bool enabled = !std::is_const<T>::value>
    typename std::enable_if<enabled, void>::type
    doSomething() {
        _doSomething();
    }
private:
    //std::vector<T> ts; https://stackoverflow.com/a/17313104/258418
    T test;
};

int main()
{
    Foo<Bar> nonConstInst;
    Foo<const Bar> ConstInst;

    nonConstInst.doSomething();
    ConstInst.doSomething();
    return 0;
}
like image 200
ted Avatar answered Sep 21 '22 11:09

ted