3

I want to write a function which distinguish between arrays and pointers. This is needed in order to figure size of literal strings. I tried:

template<typename Ty>
void f(const Ty* rhs) {
    std::cout << __FUNCTION__ << rhs << std::endl;
}

template<typename Ty, size_t Dm>
void f(const Ty(&rhs)[Dm]) {
    std::cout << __FUNCTION__ << rhs << std::endl;
}

int main(int, char*[]) {
    const char arr0[] = "test2";
    const char* ptr = "test3";
    const char arr6[6] = "test4";
    f("test1");
    f(arr0);
    f(ptr);
    f(arr6);
    return 0;
}

But the compiler (VS2013) tells me that the call is ambiguous. Any hints?

Thanks in advance.

U. Mann
  • 57
  • 2
  • 4
    It would help if you specified which of the function calls are ambiguous (I can guess, but that information should be in the question.) – juanchopanza Aug 23 '15 at 13:42

4 Answers4

3

Unfortunately, the call are ambiguous.

As workaround, you may add an extra layer:

template<typename Ty>
void f_pointer(const Ty* rhs) {
    std::cout << __FUNCTION__ << rhs << std::endl;
}

template<typename Ty, size_t Dm>
void f_array(const Ty(&rhs)[Dm]) {
    std::cout << __FUNCTION__ << rhs << std::endl;
}

template<typename T>
std::enable_if_t<std::is_array<T>::value>
f(const T&t)
{
    f_array(t);
}

template<typename T>
std::enable_if_t<!std::is_array<T>::value>
f(const T&t)
{
    f_pointer(t);
}

Live Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

An alternative to Jarod42's answer (which works) is to use class template specialization:

#include <iostream>
#include <type_traits>


template <typename T, bool is_array>
struct f_helper {
    static void print_type (T& arg) {
        std::cout << arg << " is an array\n";
    }
};

template <typename T>
struct f_helper<T, false> {
    static void print_type (T arg) {
        std::cout << arg << " is not an array\n";
    }
};

template <typename T>
void f (T& arg) {
    f_helper<T, std::is_array<T>::value>::print_type (arg);
}


int main(int, char*[]) {
    const char arr0[] = "test2";
    const char* ptr = "test3";
    const char arr6[6] = "test4";
    f("test1");
    f(arr0);
    f(ptr);
    f(arr6);
    return 0;
}

Live demo

David Hammen
  • 32,454
  • 9
  • 60
  • 108
0

Change the reference to a pointer in the second overload of the function template:

template<typename T, size_t S> void f(T(&)[S]){
  cout << "array with size " << S << "\n";
}

template <typename T> void f(T*&) {
  cout << "pointer \n";
}  

Live Demo

Midiparse
  • 4,701
  • 7
  • 28
  • 48
-3

I don't believe it's possible to do what you're trying the way you're trying. The following statements evaluate to the same thing:

  • int* var

  • int var[]

It's all a matter of syntax sugar. Moreover, adding a size to the array on the function header has only the benefit of warning the user of the expected array size. Therefore the two are also equivalent (as far as the compiler is concerned):

  • void f(int* v)

  • void f(int v[8])

Mateusz Grzejek
  • 11,698
  • 3
  • 32
  • 49
TMT_br
  • 30
  • 2
  • 2
    Note that OP expects correctly reference to array (`void f(int (&v)[8])`), which is not the same as `void f (int* v)`. – Jarod42 Aug 23 '15 at 14:16
  • Thanks everyone for clarifying this issue and for your suggestions. I find it surprising that the language specifications require an array to decay to a pointer before doing overloading resolution. Can anyone explain why this choice was made by the standards committee? – U. Mann Aug 24 '15 at 12:56
  • @U.Mann I guess that compatibility with C-style code required that: you don't want to force people to rewrite all their functions as templates if they received an array parameter. See also [here](http://stackoverflow.com/a/4810668/509868). – anatolyg Aug 24 '15 at 15:23
  • This is not a likely reason. The compiler could try to match before decaying. If overload resolution failed, try again after decaying array to pointer. – U. Mann Aug 24 '15 at 15:36