1

g++ with std=c++14 is giving me a "couldn't deduce template parameter ‘Key'" error, on a template method of a functor class (that isn't itself a template). I can't figure out why. The code looks like it should work.

I am implementing a 2 3 tree, and it has a level order traversal method that takes a functor. operator. The tree23 code is basically this:

template<class Key, class Value> class tree23 {
public:
 class Node23 {
      friend class tree23<Key, Value>;             
    public:   
    // snip...

      friend std::ostream& operator<<(std::ostream& ostr, const Node23& node23)
      { 
        // snip... outputs the keys and values of node23 to ostr.
      }

   private:

        Node23 *parent;
        std::array<Key, 2> keys;
        std::array<Value, 2> values;
        std::array<std::unique_ptr<Node23>, 3> children;

        int totalItems; // either TwoNodeItems or ThreeNodeItems
        // snip...
 }; 

   template<typename Functor> void levelOrderTraverse(Functor f) const noexcept;
   // snip...
};

The level order traversal invokes the functor's function call operator, passing it two parameters.

template<class Key, class Value> template<typename Functor> \
void tree23<Key, Value>::levelOrderTraverse(Functor f) const noexcept
{
   std::queue< std::pair<const Node23*, int> > queue; 

   Node23 *proot = root.get();

   if (proot == nullptr) return;

   auto initial_level = 1; // initial, top level is 1, the root.

   queue.push(std::make_pair(proot, initial_level));

   while (!queue.empty()) {

        std::pair<const Node23 *, int> pair_ = queue.front();

        const Node23 *current = pair_.first;

        int current_tree_level = pair_.second;

        // invokes functor's operator()(const Node23&, int)? 
        f(*current, current_tree_level);

        if (!current->isLeaf()) {

            for(auto i = 0; i < current->getChildCount(); ++i) {

               queue.push(std::make_pair(current->children[i].get(), current_tree_level + 1));  
            }
        }

        queue.pop(); 
   }
}

The functor is quite simple:

class levelOrderPrinter {

  private:
    // snip...    
  public:
         levelOrderPrinter(std::ostream& ostr_lhs, int depth); 
         levelOrderPrinter(levelOrderPrinter&);
         levelOrderPrinter(const levelOrderPrinter&);

         template<class Key, class Value> 
         void operator()(const typename tree23<Key, Value>::Node23& node,
                         int current_level) noexcept;
};

template<class Key, class Value> 
void levelOrderPrinter::operator()(const typename tree23<Key, Value>::Node23& node,
                             int current_level) noexcept
{
   // Did level change?
   if (level != current_level) { 

       level = current_level;
       ostr << "\n\n" << "level = " <<  level; 

       // Provide some basic spacing to tree appearance.
       std::size_t num = tree_depth - level + 1;

       std::string str( num, ' ');

       ostr << str;
   }

   ostr <<  node;
}
cpplearner
  • 13,776
  • 2
  • 47
  • 72
Kurt Krueckeberg
  • 1,225
  • 10
  • 11

3 Answers3

2

If you change the declaration of your function call operator like so:

template<class Node> 
void levelOrderPrinter::operator()(const Node& node, int current_level) noexcept

Then the compiler will be able to deduce the type of Node.

In your original code:

template<class Key, class Value> 
void levelOrderPrinter::operator()(const typename tree23<Key, Value>::Node23& node,
                                   int current_level) noexcept

The compiler is not able to deduce the types Key or Value because tree23<Key, Value>::Node23 is a non-deduced context for Key and Value (§14.8.2.5), forcing you to use the explicit function template call syntax.

Oktalist
  • 14,336
  • 3
  • 43
  • 63
  • Thanks. It's clear to me now that it doesn't matter at all whether the type passed as `node` happens to be a nested type of some tree class instance, and as you pointed out this caused a non-deducted context for `Key` and `Value`. – Kurt Krueckeberg Apr 06 '16 at 01:37
1

Changing this line

f(*current, current_tree_level);

in the method

template<class Key, class Value> template<typename Functor> \
void tree23<Key, Value>::levelOrderTraverse(Functor f) const  noexcept

to be

f.template operator()<Key, Value>(*current, current_tree_level); 

got rid of the "can't deduce template parameter” error. It isn't pretty but it now compiles.

Kurt Krueckeberg
  • 1,225
  • 10
  • 11
1

Node23 is a nested type, and template parameters of its enclosing type cannot be deduced that easily. So you've had to specify them explicitly via template operator().

bipll
  • 11,747
  • 1
  • 18
  • 32