1

I have small problem, because I dont know how write my template method with compare cmp like optional parametr (compare cmp is function, not variable).

It is very difficult for me, because I am new about templates in c++ and new in c++ generaly :D

I want, that when I dont use compare src, compare src return true (beacuse of if condition...) Any ideas, please? :)

   template<typename compare>
   map<_NODE, int> Find(const _NODE& src, const int& max, const compare& cmp) const {
      map<_NODE, int> finded;
      queue<_NODE> q;
      _NODE town;
      //src doesn't exists
      if (mapTown.find(src) == mapTown.end()) {
         ostringstream oss;
         oss << "unknown " << src;
         throw invalid_argument(oss.str());
      }
      //begin of queue
      q.push(src);
      finded.insert(make_pair(src, 0));
      //BFS algorithm
      while (!q.empty()) {
         town = q.front();
         q.pop();
         //we are looking for all neighborhood of town in max length
         for (const pair<_NODE, list<_EDGE>> i : (mapTown.at(town))) {
            for (const _EDGE j : i.second) {
               if (finded.count(i.first) == 0 && finded[town] < max && cmp(j)) {
                  finded.insert(make_pair(i.first, finded[town] + 1));
                  q.push(i.first);
               }
            }
         }
      }
      return finded;
   }
freedom266
  • 11
  • 2
  • @melpomene There is no even picture of code – Yksisarvinen Apr 21 '18 at 20:09
  • @freedom266 What is your question? Please show us the relevant code - [mcve]. And as a side note, templates might not be the best place to start learning C++ – Yksisarvinen Apr 21 '18 at 20:11
  • @melpomene Oops, my bad. Sorry. – Yksisarvinen Apr 21 '18 at 20:14
  • @melpomene My bad, I posted the code now :D – freedom266 Apr 21 '18 at 20:20
  • What types can `compare` be? – Killzone Kid Apr 21 '18 at 20:25
  • Note that identifiers starting with an underscore followed by a capital letter are reserved for the implementation. Your class names are illegal. Also, all caps is normally reserved for macros in C++. – D Drmmr Apr 21 '18 at 20:42
  • Side-note: `cmp` is not a comparison function as you're using it (which would almost always compare two arguments to each other). What you're using is known as a "predicate" or more colloquially, "filter function". Naming it `compare` will confuse maintainers who expect it to be an actual comparator of some sort. – ShadowRanger May 02 '18 at 19:17

4 Answers4

0

First, as others mentioned, please post relelant code as Minimal, Complete, and Verifiable example. Also as others suggested templates are probably not a good idea to start learning c++. But if you are determined you should try on something more easier to get the grasp of concepts.

As it goes for your problem, you have at least two options for how to write a function which receives a comparator. More modern approach is by using c++11's functional library and its std::function type. Second approach is by using templates, in this case an function pointer is passed to the function.

To demonstrate both options here is a minimal example:

#include <functional>

bool evaluate(int value) {
  return value > 0;
}

void function1(int value, std::function<bool(int)> eval = evaluate) {
  bool result = eval(value);
}

template<class Type = decltype(evaluate)>
void function2(int value, Type eval = evaluate) {
  bool result = eval(value);
}

int main() {
  function1(1);
  function2(1);
  return 0;
}

In the example both options receive one integer and the evaluator returns true if the integer is greater than 0. Also in both functions the evaluator is defaulted to predefined evaluate function and it can be checked (if it exists) by:

if(eval) {
  ...
}
0

How [to] write my template method with compare cmp like optional parameter

Well, given the signature you've provided:

Find(
    const _NODE&    src,
    const int&      max,
   e compiler and the standard library." Not exactly. Compiler reserved names start with double underscore or with underscore and upper letter (which is the case actually). Otherwise it's just ugly and not recommen const compare&  cmp
) const;

You'd do it this way:

template<typename compare>
map<_NODE, int> 
Find(
    const _NODE&  src,
    const int&    max,
    compare       cmp = {}
) const;

However, note that your naming practices are somewhat problematic:

  • Calling a class which is not a simple functor by a name which is a verb, e.g. Find, is somewhat confusing.
  • You are not supposed to name your own constructs with names beginning with an underscore and an uppercase letter, e.g. _NODE; that's reserved for the compiler and the standard library.
  • It's customary to use TitleCase for template parameters, so I'd use Compare rather than Compare.
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • "You are not supposed to name your own constructs with names beginning with an underscore, e.g. `_NODE;` that's reserved for the compiler and the standard library." [Not exactly.](https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) Compiler reserved names start with double underscore or with underscore and upper letter (which is the case actually). Otherwise it's just ugly and not recommended way to denote private members. – Yksisarvinen Apr 21 '18 at 20:36
  • This is school homework and name Find is stock from professor and he send some asserts to this function, so rules are very strict abou implementation :) – freedom266 Apr 21 '18 at 20:36
0

If compare is a typedef of a function pointer you could pass nullptr as default option and inside your code simply check first if function exists, if not, default to true:

#include <iostream>

typedef bool(*DefaultComparator)(int);

bool NumberIsEven(int i)
{
    return i % 2 == 0;
}

template<typename compare = DefaultComparator>
void CheckNumber(int j, compare cmp = nullptr)
{
    if (j > 0 && (cmp ? cmp(j) : true))
        std::cout << "checked" << std::endl;
    else
        std::cout << "failed" << std::endl;
}

int main()
{
    CheckNumber(1);
    CheckNumber(1, NumberIsEven);
    CheckNumber(2);
    CheckNumber(2, NumberIsEven);
}

https://ideone.com/31qNyP

checked
failed
checked
checked
Killzone Kid
  • 6,171
  • 3
  • 17
  • 37
0

The existing answers will make code that works, but as a rule, with templated functions, you don't want them to be templated on function pointers, because that inhibits optimizations. In this case, inhibiting optimizations is a (relatively) big deal, because your desired default case makes your "compare" function (really a predicate function, since it's testing a condition on a single argument, not comparing multiple arguments) a no-op; if properly optimized, the default use of your function should not call your "always true" predicate at all, because it would inline the constant result, and simplify your test condition.

To properly generate optimized templates, you want to pass functors or C++11 lambdas (which are largely syntactic sugar for functors) as your predicate. This also means making the default predicate a lambda or functor, so the default case is fully performant. Thanks to C++11 lambdas, this is pretty easy:

// Define a trivial predicate that return true for any int input
// lambdas can only be constexpr on C++17 and higher; for C++11/C++14, you can
// just declare it const, and optionally static in either case
constexpr auto alwaystrue = [](int) { return true; };

// The type of a lambda is accessible via decltype, make that the default type
// of Predicate, so you can make the default value of predicate alwaystrue itself
template<typename Predicate=decltype(alwaystrue)>
map<_NODE, int> Find(const _NODE& src, const int max, Predicate pred=alwaystrue) const {

This is a significantly better option than accepting a function pointer defaulting to nullptr or an existing alwaystrue function; accepting a function pointer defaulting to nullptr means you need to test it for nullptr over and over (or split the code path into "given predicate" vs. "not given predicate"; if you're lucky, the compiler might do that for you, but don't count on it), and accepting as a function pointer at all means the function call can't be omitted, and must be performed through a pointer, not a stable jump to a known function location, meaning all the function call setup/call/return/teardown overhead is paid every time, even if the work to be done is a constant return true;.

By writing it this way, even minimal optimizations (-O1) ensure that the default predicate of alwaystrue is optimized away entirely (it behaves as if that specialization of Find doesn't even include && cmp(j)/&& pred(j)). If the caller explicitly passes a function/function pointer, they'll usually (barring extreme link time optimizations) get the slower behavior associated with calls through function pointer but it will still work; if they know what they're doing, they'll pass a lambda or functor and get the same optimizations alwaystrue gets (inlining the test to avoid function call overhead completely, though obviously it can't optimize the actual test itself unless its as a simple as alwaystrue's test).

As an alternative approach that avoids spilling information about alwaystrue to anyone who includes your header, you can define two versions of your function, one of which accepts the predicate, and the other of which does not. Done this way, you can hide alwaystrue in your source file (or inline the raw lambda without naming it), where it won't be seen or shared with other compilation units. Done this way, you can define a non-defaulting templated function and declare (without defining) a non-templated function that doesn't take a predicate argument in your header:

template<typename Predicate>
map<_NODE, int> Find(const _NODE& src, const int max, Predicate pred) const {

map<_NODE, int> Find(const _NODE& src, const int max) const;

then in your source file (hidden from others including your header), you'd the trivial body of the non-templated function implemented in terms of the templated function:

map<_NODE, int> Find(const _NODE& src, const int max) const {
    return Find(src, max, [](int) { return true; });
}

This approach is what the C++ standard library does with functions like std::sort where the comparator is optional; template< class RandomIt > void sort( RandomIt first, RandomIt last ); is typically implemented as return sort(first, last, functor_to_compare_iterator_targets());.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271