Preferring method with size template over method with pointer type
Asked Answered
E

1

6

When overloading a method, I believe the compiler will choose the simpler match when multiple matches are available.

Consider this code:

#include <iostream>
#include <string>

struct  A {
    static void foo(const char *str) {
        std::cout << "1: " << str  << std::endl;
    }

    template<int N>  static void foo(const char (&str)[N]) {
        std::cout << "2: " << str  << std::endl;
    }
};

int main()
{
    A::foo("hello");
}

The output is 1: hello. Yet, if I comment out the static void foo(const char *str) method, it compiles fine and outputs 2: hello.

How can I have both methods on a class such that arrays with known size will call the template method, and pointer types call the non-template method?

I tried the following:

struct  A {
    template<class _Ty = char>
    static void foo(const _Ty *str) {
        std::cout << "1: " << str  << std::endl;
    }

    template<int N>  static void foo(const char (&str)[N]) {
        std::cout << "2: " << str  << std::endl;
    }
};

But g++ gives me the following error:

In function 'int main()':
17:17: error: call of overloaded 'foo(const char [6])' is ambiguous
17:17: note: candidates are:
6:15: note: static void A::foo(const _Ty*) [with _Ty = char]
10:32: note: static void A::foo(const char (&)[N]) [with int N = 6]
Eleonoraeleonore answered 24/11, 2016 at 3:9 Comment(5)
First, don't use _Ty, that's reserved to the implementation. Second, const T * const & str.Bowsprit
Thanks, that works ! As for T vs _Ty, I'll respectfully disagree. 'T' is a terrible naming choice when have to do a search for it in your text editor.Eleonoraeleonore
It's not a question of "respectfully disagreeing," it's a question of "can be broken for no apparent reason whatsoever." You are using implementation reserved typenames. #12924743Harder
gotcha - thanks for pointing it out. I'm curious why stl uses this style all over the place if there is a risk that _Ty becomes used for something else. In any case, my disagreeing was more with the use of the standalone "T" which I find makes the code difficult to manage - I can settle for Ty with no underscore.Eleonoraeleonore
@GaspardPetit any names beginning with the underscore followed by a capital letter is reserved for the STL. With that said, they can use these names without the fear of breaking user code: users are not permitted to use those. On the other hand, they could create a macro that replace every _Ty in your code by nothing. This is why you should not use these names. And this is why the standard uses it.Castro
E
1

As suggested by T.C., this works:

struct  A {

    template<class T, typename = typename std::enable_if<std::is_same<T, char>::value>::type>
    static void foo(const T * const & str) {
        std::cout << "1: " << str  << std::endl;
    }

    template<int N>  static void foo(const char (&str)[N]) {
        std::cout << "2: " << str  << std::endl;
    }
};

int main()
{
    A::foo("hello1");

    const char *c = "hello2";
    A::foo(c);

    char *c2 = new char[7];
    ::strcpy(c2, "hello3");
    A::foo(c2);

    // does not compile
    // int *c3;
    // A::foo(c3);
}

Outputs:

2: hello1
1: hello2
1: hello3

I wish I did not have to template the pointer method since it opens the door to misuses with unexpected types, but I can live with this solution.

Eleonoraeleonore answered 24/11, 2016 at 3:42 Comment(4)
const T * & str is enough?Hawaiian
If you're worried about use with unexpected types, you can use std::enable_if and std::is_same to verify that T is of type char, no?Harder
const T * & str is indeed enoughEleonoraeleonore
Actually, const T * const & str works better, since it will accept char* arguments in addition to const char*Eleonoraeleonore

© 2022 - 2024 — McMap. All rights reserved.