Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to overload a function that can tell a fixed array from a pointer?

Tags:

Motivation:

Almost for fun, I am trying to write a function overload that can tell apart whether the argument is a fixed-size array or a pointer.

double const  d[] = {1.,2.,3.}; double a; double const* p = &a; f(d); // call array version f(p); // call pointer version 

I find this particularly difficult because of the well known fact that arrays decay to pointer sooner than later. A naive approach would be to write

void f(double const* c){...} template<size_t N> void f(double const(&a)[N]){...} 

Unfortunately this doesn't work. Because in the best case the compiler determines the array call f(d) above to be ambiguous.

Partial solution:

I tried many things and the closest I could get was the following concrete code. Note also that, in this example code I use char instead of double, but it is very similar at the end.

First, I have to use SFINAE to disable conversions (from array ref to ptr) in the pointer version of the function. Second I had to overload for all possible arrays sizes (manually).

[compilable code]

#include<type_traits> // for enable_if (use boost enable_if in C++98) #include<iostream> template<class Char, typename = typename std::enable_if<std::is_same<Char, char>::value>::type> void f(Char const* dptr){std::cout << "ptr" << std::endl;} // preferred it seems  void f(char const (&darr)[0] ){std::cout << "const arr" << std::endl;} void f(char const (&darr)[1] ){std::cout << "const arr" << std::endl;} void f(char const (&darr)[2] ){std::cout << "const arr" << std::endl;} void f(char const (&darr)[3] ){std::cout << "const arr" << std::endl;} void f(char const (&darr)[4] ){std::cout << "const arr" << std::endl;} void f(char const (&darr)[5] ){std::cout << "const arr" << std::endl;} void f(char const (&darr)[6] ){std::cout << "const arr" << std::endl;} // this is the one called in this particular example // ad infinitum ...  int main(){          f("hello"); // print ptr, ok because this is the fixed size array     f(std::string("hello").c_str()); // print arr, ok because `c_str()` is a pointer } 

This works, but the problem is that I have to repeat the function for all possible values of N and using template<size_t N> gets me back to square zero, because with the template parameter the two calls get back to equal footing. In other words, template<size_t N> void f(char const(&a)[N]){std::cout << "const arr" << std::endl;} doesn't help.

Is there any way to generalize the second overload without falling back to an ambiguous call? or is there some other approach?

A C++ or C++1XYZ answer is also welcome.

Two details: 1) I used clang for the experiments above, 2) the actual f will end up being an operator<<, I think know if that will matter for the solution.


Summary of solutions (based on other people's below) and adapted to a the concrete type char of the example. Both seem to depend on making the char const* pointer less obvious for the compiler:

  1. One weird (portable?), (from the comment of @dyp.) Adding a reference qualifier to the pointer version:
    template<class Char, typename = typename std::enable_if<std::is_same<Char, char>::value>::type>     void f(Char const* const& dptr){std::cout << "ptr" << std::endl;}          template<size_t N>     void f(char const (&darr)[N] ){std::cout << "const arr" << std::endl;} 
  1. One elegant (special case from @user657267)
    template<class CharConstPtr, typename = typename std::enable_if<std::is_same<CharConstPtr, char const*>::value>::type>     void f(CharConstPtr dptr){std::cout << "ptr" << std::endl;}          template<size_t N>     void f(char const (&darr)[N] ){std::cout << "const arr" << std::endl;} 
like image 581
alfC Avatar asked Jan 28 '15 00:01

alfC


People also ask

In which we Cannot overload the function?

In which of the following we cannot overload the function? Explanation: While overloading the return function, it will rise a error, So we can't overload the return function.

Is it possible to overload a function in C++?

C++ lets you specify more than one function of the same name in the same scope. These functions are called overloaded functions, or overloads. Overloaded functions enable you to supply different semantics for a function, depending on the types and number of its arguments.

What are the conditions to overload a function?

If any class has multiple functions with different parameters having the same name, they are said to be overloaded. If we have to perform a single operation with different numbers or types of arguments, we need to overload the function.

When function is overloaded which part of the function are the same?

Function Overloading is when multiple function with same name exist in a class. Function Overriding is when function have same prototype in base class as well as derived class. 2. Function Overloading can occur without inheritance.


2 Answers

This seems to work for me

#include <iostream>  template<typename T> std::enable_if_t<std::is_pointer<T>::value> foo(T) { std::cout << "pointer\n"; }  template<typename T, std::size_t sz> void foo(T(&)[sz]) { std::cout << "array\n"; }  int main() {   char const* c;   foo(c);   foo("hello"); } 

Bonus std::experimental::type_traits:

using std::experimental::is_pointer_v; std::enable_if_t<is_pointer_v<T>> 

Your comment made me try something even simpler

template<typename T> void foo(T) { std::cout << "pointer\n"; } template<typename T, unsigned sz> void foo(T(&)[sz]) { std::cout << "array\n"; } 

Of course the problem here is that foo is now callable for any type, depends on how lax you want your parameter checking to be.


One other way is to (ab)use rvalue references

void foo(char const*&) { std::cout << "pointer\n"; } void foo(char const*&&) { std::cout << "array\n"; } 

Obviously it's not foolproof.

like image 137
user657267 Avatar answered Sep 19 '22 00:09

user657267


You may use the following:

namespace detail {     template <typename T> struct helper;      template <typename T> struct helper<T*> { void operator() () const {std::cout << "pointer\n";} };     template <typename T, std::size_t N> struct helper<T[N]> { void operator() ()const {std::cout << "array\n";} }; }   template <typename T> void f(const T& ) {     detail::helper<T>{}(); } 

Live example

like image 21
Jarod42 Avatar answered Sep 21 '22 00:09

Jarod42