CS253: Software Development with C++

Spring 2021

Typename

Show Lecture.Typename as a slide show.

CS253 Typename

🍨

Requirement

set<short> s;
cout << sizeof(s.size());
8

First pass

template <class T>
struct Answer {
    set<T>::size_type value = 42;
};

int main() {
    Answer<short> doug;
    cout << doug.value;
}
c.cc:3: error: need 'typename' before 'std::set<T>::size_type' because 
   'std::set<T>' is a dependent scope

Second pass

template <class T>
struct Answer {
    typename set<T>::size_type value = 42;
};

int main() {
    Answer<short> doug;
    cout << doug.value;
}
42
     using newtype = typename oldtype;
     typedef typename oldtype newtype;

Why?

“But, Jack, there are other reasons for not eating ice cream!” 🙄
  • Thank you. We all know this.
  • Some people avoid ice cream for medical or ethical reasons.
  • This was one possible reason. It’s not all about you.
  • A → B does not mean that B → A.

Practical use

template <class T>
class fussyset {
    set<T> store;
  public:
    using iterator = set<T>::iterator;
    void insert(T val) { if (val != "ice cream") store.insert(val); }
    iterator begin() const { return store.begin(); }
    iterator end() const { return store.end(); }
};

int main() {
    fussyset<string> fs;
    fs.insert("cereal");    fs.insert("apple");
    fs.insert("ice cream"); fs.insert("apple");

    for (auto val : fs)
        cout << val << '\n';
}
c.cc:5: error: need 'typename' before 'std::set<T>::iterator' because 
   'std::set<T>' is a dependent scope

Practical use

template <class T>
class fussyset {
    set<T> store;
  public:
    using iterator = typename set<T>::iterator;
    void insert(T val) { if (val != "ice cream") store.insert(val); }
    iterator begin() const { return store.begin(); }
    iterator end() const { return store.end(); }
};

int main() {
    fussyset<string> fs;
    fs.insert("cereal");    fs.insert("apple");
    fs.insert("ice cream"); fs.insert("apple");

    for (auto val : fs)
        cout << val << '\n';
}
apple
cereal

Has-a vs. Is-a

Is-a

template <class T>
class fussyset : private set<T> {   // PRIVATE inheritance‽
    using super = set<T>;
  public:
    using iterator = typename super::iterator;
    void insert(T val) { if (val != "ice cream") super::insert(val); }
    iterator begin() const { return super::begin(); }
    iterator end() const { return super::end(); }
};

int main() {
    fussyset<string> fs;
    fs.insert("apple");     fs.insert("cereal");
    fs.insert("ice cream"); fs.insert("apple");

    for (auto val : fs)
        cout << val << '\n';
}
apple
cereal

Those forwarding methods are still a pain, and WET, repeating the return types, constness, and arguments.

Is-a

template <class T>
class fussyset : private set<T> {
    using super = set<T>;
  public:
    using super::iterator, super::begin, super::end;  // public
    void insert(T val) { if (val != "ice cream") super::insert(val); }
};

int main() {
    fussyset<string> fs;
    fs.insert("apple");     fs.insert("cereal");
    fs.insert("ice cream"); fs.insert("apple");

    for (auto val : fs)
        cout << val << '\n';
}
apple
cereal

Much better. This way, we know that the types & arguments of the fussyset methods are the same as those of the set<T> methods. No typename, either!