11

I want to be able to differentiate array from pointers in overload resolution :

class string {
public:
        string(const char* c_str);

        template<int N>
        string(const char (&str) [N]);
};


int main() {
        const char* c_str = "foo";
        string foo(c_str);      // ok will call string(const char*)

        string bar("bar");      // call string(const char*) instead of the array version
}

The best I have found so far is to use a reference to the pointer instead of a pointer :

class string {
public:
        string(const char*& c_str);

        template<int N>
        string(const char (&str) [N]);
};


int main() {
        const char* c_str = "foo";
        string foo(c_str);      // ok will call string(const char*)
        string bar("bar");      // ok, will call the array version
}

it's not exactly the same thing and I want to know if a better way exist

8
  • Why do you expect a difference between the calls, and what's your intend to use such a (non-existent) side effect? BTW naming a class string outside of dedicate namespace is calling for trouble. Commented Feb 23, 2014 at 18:41
  • I expect differences because, when i call string bar("bar"), I don't pass a pointer but an array, that's were the array to pointer decay kicks in and I want to get the size of the array at compile time if it's available, memcpy is far better than strcpy. Otherwise, this class has it's proper namespace ofc, I drop it for clarity. Commented Feb 23, 2014 at 18:44
  • If the array is static I can, try the second snippet of code, you will see, the constructor with templated array parameter is called on the second call (string bar("bar")) . what I want is a better way to discriminate pointer from arrays. Commented Feb 23, 2014 at 18:57
  • Is an init function ok? I'm not sure how to do it with constructors. Commented Feb 23, 2014 at 19:05
  • It will be better with constructor but it's always good to know how to do that :) Commented Feb 23, 2014 at 19:08

3 Answers 3

13

You need to make the first overload a poorer choice when both are viable. Currently they are a tie on conversion ranking (both are "Exact Match"), and then the tie is broken because non-templates are preferred.

This ought to make the conversion ranking worse:

struct stg
{
    struct cvt { const char* p; cvt(const char* p_p) : p(p_p) {} };

    // matches const char*, but disfavored in overload ranking
    stg(cvt c_str); // use c_str.p inside :(  Or add an implicit conversion

    template<int N>
    stg(const char (&str) [N]);
};
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, I don't know why I forgot this option, it's more readable.
6

You can use SFINAE. This might not be the best way, but it should work ok:

//thanks to dyp for further reduction
template<typename T, typename = typename std::enable_if<std::is_same<T, char>::value>::type>
string(const T * const &) {std::cout << "const char *\n";}

template<std::size_t N> //credit to jrok for noticing the unnecessary SFINAE
string(const char(&)[N]) {std::cout << "const char(&)[" << N << "]\n";}

Here's a live example.

13 Comments

Indeed it will do exactly the same thing as passing a reference to the pointer because the signature of the friend function is string make_string(const T &). Maybe that's the only way, I'm trying with SFINAE but I have no result so far. Thank you for your answer
@pan-, I've since updated my answer to use SFINAE, since it's doing the same thing and I just didn't realize it before. There's another update now to allow for char *.
I've seen the update after I reply, it's far better, and it can work on non reference to pointer : coliru.stacked-crooked.com/a/1c8faf4816d1b84c thank you very much
@pan-, Ah, good point. Make sure you get the updated code if you want that to do char * as well. I'm not aware of any better way to add it in than a separate check altogether since the const is hidden behind a pointer. Maybe std::remove_pointer, but then you have to check that it's a pointer to begin with for it to work.
|
0

A more generic version of this problem could be detected as follows.

template <class T>
void func(T, 
          typename std::enable_if<std::is_pointer<T>::value, void>::type * = 0)
{
  // catch ptr
}

template <class T, int N>
void func(T (&)[N])
{
  //catch array
}

int main(void)
{
  int arr[5];
  char *b = 0;
  func(arr); // catch array
  func(b);   // catch ptr
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.