1

I'm looking into the c++ source code of a library I'm using, full source available here. I've got a fair idea of how c++ templates work, however I have no idea why one would use a typename within a function body, i.e. in the code example below, why would one put typename before unordered_map<Token*, BaseFloat>::const_iterator iter = final_costs.find(tok);?

NB: No I don't consider this a duplicate, It's a specific question in order to understand why typename is used within the function body for given function. I notice people mark it as duplicate and then refer to a generic template question such as where / when to use template / typename keywords, I've been through the info of provided links, however I haven't seen the answer there, so if marking as duplicate please be specific as to why you think it answers the question here.

template <typename FST>
typename LatticeFasterOnlineDecoderTpl<FST>::BestPathIterator LatticeFasterOnlineDecoderTpl<FST>::BestPathEnd(
...
.....
      typename unordered_map<Token*, BaseFloat>::const_iterator
          iter = final_costs.find(tok);
...
....

The full code block of the above function:

template <typename FST>
typename LatticeFasterOnlineDecoderTpl<FST>::BestPathIterator LatticeFasterOnlineDecoderTpl<FST>::BestPathEnd(
    bool use_final_probs,
    BaseFloat *final_cost_out) const {
  if (this->decoding_finalized_ && !use_final_probs)
    KALDI_ERR << "You cannot call FinalizeDecoding() and then call "
              << "BestPathEnd() with use_final_probs == false";
  KALDI_ASSERT(this->NumFramesDecoded() > 0 &&
               "You cannot call BestPathEnd if no frames were decoded.");

  unordered_map<Token*, BaseFloat> final_costs_local;

  const unordered_map<Token*, BaseFloat> &final_costs =
      (this->decoding_finalized_ ? this->final_costs_ :final_costs_local);
  if (!this->decoding_finalized_ && use_final_probs)
    this->ComputeFinalCosts(&final_costs_local, NULL, NULL);

  // Singly linked list of tokens on last frame (access list through "next"
  // pointer).
  BaseFloat best_cost = std::numeric_limits<BaseFloat>::infinity();
  BaseFloat best_final_cost = 0;
  Token *best_tok = NULL;
  for (Token *tok = this->active_toks_.back().toks;
       tok != NULL; tok = tok->next) {
    BaseFloat cost = tok->tot_cost, final_cost = 0.0;
    if (use_final_probs && !final_costs.empty()) {
      // if we are instructed to use final-probs, and any final tokens were
      // active on final frame, include the final-prob in the cost of the token.
      typename unordered_map<Token*, BaseFloat>::const_iterator
          iter = final_costs.find(tok);
      if (iter != final_costs.end()) {
        final_cost = iter->second;
        cost += final_cost;
      } else {
        cost = std::numeric_limits<BaseFloat>::infinity();
      }
    }
    if (cost < best_cost) {
      best_cost = cost;
      best_tok = tok;
      best_final_cost = final_cost;
    }
  }
  if (best_tok == NULL) {  // this should not happen, and is likely a code error or
    // caused by infinities in likelihoods, but I'm not making
    // it a fatal error for now.
    KALDI_WARN << "No final token found.";
  }
  if (final_cost_out)
    *final_cost_out = best_final_cost;
  return BestPathIterator(best_tok, this->NumFramesDecoded() - 1);
}
Gio
  • 3,242
  • 1
  • 25
  • 53
  • Whether `unordered_map::const_iterator` requires `typename` likely depends on the `.....` lines of code that have been removed from this question. – Drew Dormann May 23 '19 at 22:26
  • @Drew I've now include the full code-block, @Fureeish I don't see this as a duplicate, you're link refers to quite a general template question, it contains quite some good info however I haven't been able to figure out how it answers my question. Note, my question is specific, it refers to understanding, why `typename` is used / needed in within the function body of given function. – Gio May 23 '19 at 22:30

1 Answers1

1

The typename keyword is not used just to specify template arguments but also to qualify a dependent name as a type instead that a value.

If the compiler is not able to tell if unordered_map<Token*, BaseFloat>::const_iterator is a type or a value, then it assumes it's a value. Which is not the case in this situation.

So, to tell it that it's a type instead, the keyword typename is used.

This is clearly explained here.

Jack
  • 131,802
  • 30
  • 241
  • 343
  • I don't see that in this situation, i.e. Basefloat is float or double depending on the system architecture, and Token is a class of type decoder::BackpointerToken, i.e. I don't see what is the `dependent name` here or why the compiler would see the unordered_map as a value instead of type. – Gio May 24 '19 at 07:56
  • For example, if you look a couple of lines back, you see `unordered_map final_costs_local;` without `typename`, don't see how the `const_iterator` makes a difference that implies `typename` keyword to be needed. – Gio May 24 '19 at 08:09
  • @Gio, have you confirmed that `typename` is "needed" there? If your question is simply why one _might_ put it there, the answer is speculative, but it could be for readability. Or it could be that earlier code required it and the current code simply doesn't require it to be removed. – Drew Dormann May 24 '19 at 18:39
  • it's difficult to confirm in this case, I've did some testing locally and noticed I could leave it out. However it's not the exact same code I'm using, as testing the exact codebase would be a bit too much work in this case, it's merely a library I'm using, I was figuring out what the function in this library is doing and therefore I was looking at the source and hence I stumbled over the typename issue. My question indeed regards, why it's there in this specific case. – Gio May 24 '19 at 21:59