0
    template<class T, template<typename> class Seq>
    class SequenceWithApply : public Seq<T*>
    {
    public:
      // 0 arguments, any type of return value:
      template<class R>
      void apply(R (T::*f)()) {
        iterator it = begin();
        while(it != end()) {
          ((*it)->*f)();
          it++; }
      }

      // 1 argument, any type of return value:
      template<class R, class A>
      void apply(R(T::*f)(A), A a) {
        iterator it = begin();
        while(it != end()) {
          ((*it)->*f)(a);
          it++; }
      }

      // 2 arguments, any type of return value:
      template<class R, class A1, class A2>
      void apply(R(T::*f)(A1, A2),
                 A1 a1, A2 a2) {
        iterator it = begin();
        while(it != end()) {
          ((*it)->*f)(a1, a2);
          it++;
        }
      }
    }; ///:~

//: C03:applyGromit2.cpp
// Test applyMember.h
#include "Gromit.h"
#include "applyMember.h"
#include <vector>
#include <iostream>
using namespace std;
int main() {
  SequenceWithApply<Gromit, vector> dogs;
  for(int i = 0; i < 5; i++)
    dogs.push_back(new Gromit(i));
  dogs.apply(&Gromit::speak, 1);
  dogs.apply(&Gromit::eat, 2.0f);
  dogs.apply(&Gromit::sleep, 'z', 3.0);
  dogs.apply(&Gromit::sit);
} ///:~

I did not quite understand why compiler complain about iterator here. Since this snippet code implemente a class SequenceWithApply based on the template. In this case, SequenceWithApply is actually a based class of vector. iterator should be visible in this base class. I really appreciate that someone can help me figure this out.

zhexuany
  • 100
  • 6
  • I know there's a dupe somewhere out there, but `typename Seq::iterator`. – chris May 13 '16 at 00:06
  • It is not just `iterator`, but also `begin()` and `end()`. I have no clue why this does not work. :( – zhexuany May 13 '16 at 00:09
  • If I could find a dupe, it would explain that these names are looked up before the template is instantiated, which means that it can't know which concrete base type to look in. – chris May 13 '16 at 00:10
  • @chris you should create a canonical duplicate :) – Brian Bi May 13 '16 at 00:11
  • @chris So This post is a duplicated question? If it is, I will delete this post. :) – zhexuany May 13 '16 at 00:18
  • No, we don't delete duplicate questions, we just redirect them to the duplicate. But it's hard to find a good one in this case – Brian Bi May 13 '16 at 00:19
  • Is there any reason not to simply write the apply function as a free template function? the standard already has `std::for_each(iter, iter, func)`. – Richard Hodges May 13 '16 at 01:09
  • @RichardHodges I am learning template programming right now. This is a snippet I copied from books. – zhexuany May 13 '16 at 01:19
  • @zhexuany ok, in that case I'll post an alternate way of handling this, just for the sake of interest – Richard Hodges May 13 '16 at 01:22

2 Answers2

2

The compiler looks for iterator on first-phase lookup, which is before the template is ever instantiated. In order to know from which type the class derives, the template must be instantiated (so that Seq<T*> is an actual type). Thus, the compiler never finds iterator in the base class yet.

You can get around this in two easy ways:

A one-off:

typename Seq<T*>::iterator

A type alias in your derived class:

using iterator = typename Seq<T*>::iterator;

All of these unambiguously specify to which type iterator belongs, looked up in the second phase of lookup when Seq and T are known. More on typename.

You can do the same for your functions:

A one-off:

Seq<T*>::begin()
this->begin() // if inside a member function

A using declaration:

using Seq<T*>::begin;
Community
  • 1
  • 1
chris
  • 60,560
  • 13
  • 143
  • 205
  • Thanks for your post. We also need add `Seq::` to `begin()` and `end()`. In addition, there is another question about the code. On the first line of this snippet, we need add variadic templates. ie. `template class Seq>` – zhexuany May 13 '16 at 00:32
  • @zhexuany, `std::vector` has more than one type parameter. Other standard containers have differing numbers. To support their usage more uniformly, you can accept any number of type parameters. – chris May 13 '16 at 00:38
  • Yeah. Your answer is great. Thanks for your kindly help. – zhexuany May 13 '16 at 00:41
  • `decltype(*this)` is going to be a reference type that you can't use `::` on. – T.C. May 15 '16 at 21:27
  • @T.C., That's what I get for not instantiating the template when testing. I removed them. While `std::decay_t::iterator` should work to not repeat the type, it's getting a bit ridiculous at that point. – chris May 15 '16 at 21:33
0

The book you're using may be out of date. These days c++ has moved in the direction of using free functions for better decoupling.

Example:

#include <vector>
#include <iostream>

// an example Gromit
struct Gromit
{
    Gromit(int index) : index(index) {};

    void speak(int i) { std::cout << name() << " speaking " << i << std::endl; }
    void eat(float f) { std::cout << name() << " eating " << f << std::endl; }
    void sleep(char c, double f) { std::cout << name() << " sleeping " << c << " " << f << std::endl; }
    void sit() { std::cout << name() << " sitting" << std::endl; }

private:
    std::string name() const {
        return "Gromit " + std::to_string(index);
    }
    int index;
};

// apply some function object to each item in a container    
template<class Container, class F>
void apply(Container& container, F f)
{
    for (const auto& p : container)
    {
        f(p);
    }

}

int main() {
    std::vector<std::unique_ptr<Gromit>> dogs;
    for(int i = 0; i < 5; i++)
        dogs.emplace_back(new Gromit(i));

    using namespace std::placeholders;

    // apply dog.speak(1) to each dog in dogs...        
    apply(dogs, std::bind(&Gromit::speak, _1, 1));

    // dog.eat(2.0f) for each dog in dogs...
    apply(dogs, std::bind(&Gromit::eat, _1, 2.0f));

    // ...etc
    apply(dogs, std::bind(&Gromit::sleep, _1, 'z', 3.0));
    apply(dogs, std::bind(&Gromit::sit, _1));
}

expected output:

Gromit 0 speaking 1
Gromit 1 speaking 1
Gromit 2 speaking 1
Gromit 3 speaking 1
Gromit 4 speaking 1
Gromit 0 eating 2
Gromit 1 eating 2
Gromit 2 eating 2
Gromit 3 eating 2
Gromit 4 eating 2
Gromit 0 sleeping z 3
Gromit 1 sleeping z 3
Gromit 2 sleeping z 3
Gromit 3 sleeping z 3
Gromit 4 sleeping z 3
Gromit 0 sitting
Gromit 1 sitting
Gromit 2 sitting
Gromit 3 sitting
Gromit 4 sitting
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142