Search code examples
c++pointerstemplatesfunction-pointersdefault-arguments

How to add void/null as default argument to a function/lambda pointer, in C++?


Present signature is

template<class TypeData,typename TypeFunc>
bool isPrime(const TypeData& n,TypeFunc fSqrt,bool debug = false)

and this works perfectly with

std::cout<<(isPrime(n,fSqrt)?"Positive":"Negative")<<'\n';

But, my intension is something like

template<class TypeData,typename TypeFunc>
bool isPrime(const TypeData& n,TypeFunc fSqrt = nullptr,bool debug = false)

or

template<class TypeData,typename TypeFunc>
bool isPrime(const TypeData& n,TypeFunc fSqrt = NULL,bool debug = false)

to be called by

std::cout<<(isPrime(n)?"Positive":"Negative")<<'\n';

Overloading is not possible due to a static variable inside the function.
Only different class TypeData should give different template-functions for this function-template.

Please help me out with the proper syntax. If C++ does not support this, what is an alternative approach I can use?

Compile Errors

for TypeFunc fSqrt = nullptr

main.cpp:90:23: error: no matching function for call to ‘isPrime(int&)’
  std::cout<<(isPrime(n)?"Positive":"Negative")<<'\n';
                       ^
main.cpp:9:49: note: candidate: template bool isPrime(const TypeDate&, TypeFunc, bool)
 template<class TypeDate,typename TypeFunc> bool isPrime(const TypeDate& n,TypeFunc fSqrt = nullptr,bool debug = false) {
                                                 ^~~~~~~
main.cpp:9:49: note:   template argument deduction/substitution failed:
main.cpp:90:23: note:   couldn't deduce template parameter ‘TypeFunc’
  std::cout<<(isPrime(n)?"Positive":"Negative")<<'\n';
                       ^

for TypeFunc fSqrt = NULL

main.cpp:90:23: error: no matching function for call to ‘isPrime(int&)’
  std::cout<<(isPrime(n)?"Positive":"Negative")<<'\n';
                       ^
main.cpp:9:49: note: candidate: template bool isPrime(const TypeDate&, TypeFunc, bool)
 template<class TypeDate,typename TypeFunc> bool isPrime(const TypeDate& n,TypeFunc fSqrt = NULL,bool debug = false) {
                                                 ^~~~~~~
main.cpp:9:49: note:   template argument deduction/substitution failed:
main.cpp:90:23: note:   couldn't deduce template parameter ‘TypeFunc’
  std::cout<<(isPrime(n)?"Positive":"Negative")<<'\n';
                       ^

They are basically the same.


Solution

  • Overloading actually is an option, you can let one overload call the other one:

    template<class TypeData, typename TypeFunc>
    bool isPrime(const TypeData& n, TypeFunc fSqrt, bool debug = false);
    
    template<class TypeData>
    bool isPrime(const TypeData& n, bool debug = false)
    {
        using std::sqrt;
        if constexpr (std::is_integral_v<TypeData>)
        {
            return isPrime(n, static_cast<double(*)(double)>(sqrt), debug);
        }
        else if constexpr (std::is_floating_point_v<TypeData>)
        {
            return isPrime(n, static_cast<TypeData(*)(TypeData)>(sqrt), debug);
        }
        else
        {
            // this covers e.g. std::complex
            return isPrime(n, static_cast<TypeData(*)(TypeData const&)>(sqrt), debug);
            // for any other type we assume the overload accepts by
            // const reference as well (if there's one at all...)
            // if not, we still can fall back to the other overload
        }
    }
    

    This solution selects an appropriate square root function right away, something you would have had to solve anyway if the function argument had defaulted to nullptr.