1

I know there was already Default value on generic predicate as argument.

But maybe there are new options now with c++11, c++14, or c++17?

How can i make this work without overload?

#include <vector>
#include <numeric>
#include <algorithm>
#include <iterator>
#include <iostream>

#if 0

template <typename CONTAINER, typename PRED>
void print(CONTAINER const & cont, PRED pred = [] (typename CONTAINER::value_type) { return true;})
{
    std::copy_if(cont.begin(), cont.end(), std::ostream_iterator<typename CONTAINER::value_type>(std::cout, " "), pred);
    std::cout << std::endl;
}

#else

template <typename CONTAINER, typename PRED>
void print(CONTAINER const & cont, PRED pred)
{
    std::copy_if(cont.begin(), cont.end(), std::ostream_iterator<typename CONTAINER::value_type>(std::cout, " "), pred);
    std::cout << std::endl;
}

template <typename CONTAINER>
void print (CONTAINER const & cont)
{
    std::copy(cont.begin(), cont.end(), std::ostream_iterator<typename CONTAINER::value_type>(std::cout, " "));
    std::cout << std::endl;
}

#endif

bool
even(
        const int& i)
{
    return not (i % 2);
}

int
main(
        int argc,
        char **argv)
{
    std::vector<int> myVec(20);
    std::iota(myVec.begin(), myVec.end(), 1);

    print(myVec);

    print(myVec, even);

    return 0;
}

Enabling #if 0 section results in
../main.cpp:17:6: note: template argument deduction/substitution failed:
../main.cpp:56:13: note: couldn't deduce template parameter ‘PRED’

Frank Bergemann
  • 325
  • 2
  • 14
  • I don't know how to avoid the overload, but you could have single-parameter `print` call two-parameter `print`, and thus avoid duplicating the logic. – Igor Tandetnik Aug 08 '20 at 18:20
  • This would be an option. But i am looking for a base/generic type for the predicate to do w/o template parameter. – Frank Bergemann Aug 08 '20 at 18:28

3 Answers3

2

Something like this, perhaps:

template <typename CONTAINER>
void print(CONTAINER const & cont,
           std::function<bool(typename CONTAINER::value_type)> pred =
           [] (typename CONTAINER::value_type) { return true;})
{
    std::copy_if(cont.begin(), cont.end(),
                 std::ostream_iterator<typename CONTAINER::value_type>(std::cout, " "),
                 pred);
    std::cout << std::endl;
}

Demo. Not particularly efficient, but it does appear to satisfy your requirements.

Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • thanks! - this is what i was originally looking for. But you're right it always calls the more specific std::copy_if() – Frank Bergemann Aug 08 '20 at 19:48
  • The inefficiency is not in `copy_if`, it's in `std::function`. Type erasure requires a heap allocation and virtual function calls, whereas in the original template-based formulation the predicate would most likely get inlined. – Igor Tandetnik Aug 08 '20 at 19:51
1

This may not be particularly helpful to you now, but you can make it work in C++20. There were several changes in the standard surrounding lambdas. In particular, lambdas can appear in unevaluated operands and template arguments, and capture-less lambdas are default constructible. So putting it to use we can simply write

template <typename CONTAINER, typename PRED = decltype([] (typename CONTAINER::value_type) { return true;})>
void print(CONTAINER const & cont, PRED pred = {})
{
    std::copy_if(cont.begin(), cont.end(), std::ostream_iterator<typename CONTAINER::value_type>(std::cout, " "), pred);
    std::cout << std::endl;
}
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
0

Igor proposed one - which is closer to what i intended originally (thanks!)
Here's another one:

#include <vector>
#include <numeric>
#include <algorithm>
#include <iterator>
#include <iostream>

template <typename CONTAINER, typename...OPT_PRED>
void print(
        CONTAINER const & cont,
        OPT_PRED... optPred)
{
    if constexpr(sizeof...(OPT_PRED) == 1)
    {
        std::copy_if(cont.begin(), cont.end(), std::ostream_iterator<typename CONTAINER::value_type>(std::cout, " "), std::forward<OPT_PRED>(optPred)...);
    }
    else if constexpr(sizeof...(OPT_PRED) == 0)
    {
        std::copy(cont.begin(), cont.end(), std::ostream_iterator<typename CONTAINER::value_type>(std::cout, " "));
    }
    else
    {
        static_assert(sizeof...(OPT_PRED) < 0 or sizeof...(OPT_PRED) > 1, "invalid number of arguments for print()");
    }

    std::cout << std::endl;
}

bool
even(
        const int& i)
{
    return not (i % 2);
}

int
main(
        int argc,
        char **argv)
{
    std::vector<int> myVec(20);
    std::iota(myVec.begin(), myVec.end(), 1);

    print(myVec);

    print(myVec, even);

    return 0;
}
Frank Bergemann
  • 325
  • 2
  • 14