Как я могу предотвратить угадывание C ++ второго аргумента шаблона?

26

Я использую библиотеку C ++ ( strf ), которая где-то внутри имеет следующий код:

namespace strf {
template <typename ForwardIt>
inline auto range(ForwardIt begin, ForwardIt end) { /* ... */ }

template <typename Range, typename CharT>
inline auto range(const Range& range, const CharT* sep) { /* ... */ }
}

Теперь я хочу использовать strf::range<const char*>(some_char_ptr, some_char_ptr + some_length)в своем коде. Но если я это сделаю, я получу следующую ошибку (с NVCC CUDA 10.1):

error: more than one instance of overloaded function "strf::range" matches the argument list:
            function template "auto strf::range(ForwardIt, ForwardIt)"
            function template "auto strf::range(const Range &, const CharT *)"
            argument types are: (util::constexpr_string::const_iterator, util::constexpr_string::const_iterator)

Код библиотеки, вероятно, можно изменить, чтобы избежать этого (например, используя:

inline auto range(const typename std::enable_if<not std::is_pointer<typename std::remove_cv<Range>::type>::value, Range &>::type range, const CharT* sep)

чтобы Rangeне указатель); но я не могу сделать это изменение прямо сейчас. Вместо этого я хочу как-то указать компилятору, что я действительно хочу иметь только один аргумент шаблона, а не один указанный и еще один выведенный.

Я могу это сделать?

Был бы признателен за ответы для C ++ 11 и C ++ 14; Ответы C ++ 17, включающие руководства по выводам, менее актуальны, но если они у вас есть, отправьте их (для будущих версий NVCC ...)


Обновление: Сама библиотека strf была обновлена, чтобы обойти эту ситуацию, но вопрос остается неизменным.

einpoklum
источник
1
Я предполагаю, что передача пользовательского итератора, который тонко оборачивает, char*но не является ли это решением?
Конрад Рудольф
1
@KonradRudolph: Это обходной путь, но не отвечает на мой вопрос. На самом деле у меня уже есть другой обходной путь (конкретный к тому, что находится в /*...*/), но я хотел бы пойти по большой дороге здесь.
einpoklum
1
В этом случае мой (предполагаемый) ответ - «не может быть сделано», к сожалению. Честно говоря, я не уверен, что приму предложенный мной обходной путь в своем собственном коде.
Конрад Рудольф
Просто для пояснения: хотите ли вы общее решение, которое бы всегда работало, чтобы дифференцировать вызов между перегрузками шаблонов с одним параметром против двух, или вам нужно только решение, характерное для этого случая?
грецкий орех
@walnut: Общее решение будет лучше; мой конкретный сценарий в основном мотивация проблемы.
einpoklum

Ответы:

16
template<typename T>
inline constexpr auto range1_ptr = strf::range<T>;

template<typename T>
inline decltype(auto) range1(T begin, T end) {
    return range1_ptr<T>(begin, end);
}

Тогда звони range1вместо strf::range.

range1_ptr<T>(...)всегда может использоваться для явного вызова шаблона с одним аргументом шаблона, но не делает никаких выводов из аргументов. range1повторяет вычет из исходного strf::rangeшаблона.

Это работает, потому что [temp.deduct.funcaddr] / 1 говорит, что вычитание аргумента шаблона при получении адреса функции без целевого типа преобразования выполняется для каждого шаблона функции-кандидата, как если бы списки параметров и аргументов гипотетического вызова были опорожнить. Таким образом, второй аргумент шаблона не может быть выведен для второй перегрузки с двумя параметрами шаблона. Единственный оставшийся кандидат - это первая перегрузка, которая будет выбрана в качестве цели указателя функции.

Пока не существует второго шаблона функции-кандидата, для которого можно сформировать действительный идентификатор шаблона только с одним аргументом, range1_ptrвсегда можно использовать для вызова шаблона функции, принимающего один аргумент однозначно. В противном случае создание экземпляра range1_ptrдаст ошибку из-за неоднозначности.

грецкий орех
источник
Не будет ли двусмысленности strf::range<T>?
einpoklum
1
@einpoklum Отлично компилируется на GCC и Clang. Я не проверял стандарт, но был бы удивлен, если это должно быть неоднозначным.
орех
Может быть, вы должны изменить имя функции на pretty_please_with_sugar_on_top()? ... C ++ иногда бывает таким странным ...
einpoklum
Вам удалось отменить принятый ответ :-P
einpoklum
11

Как насчет прохождения через using?

using tfp = void(*)(char const *, char const *);

tfp x = &strf::range;

char const * a = "abcd";

(*x)(a, a+2);
max66
источник
А это компилирует? Вторая строка выглядит особенно подозрительно.
einpoklum
@einpoklum - забавно, не правда ли?
max66
@einpoklum - к сожалению, это не общее решение; работает в этом случае, потому что (если я не ошибаюсь) range()совместима только первая версия tpf; другой случай может быть другим.
max66
@einpoklum - во второй строке вы также можете указать параметр шаблона ( tfp x = &strf::range<char const *>;); таким образом, я полагаю, у вас есть общее решение, почти эквивалентное грецкому ореху
max66
0

Решение

1) прежде всего, вы должны указать тип для второго аргумента, например (char *)(some_char_ptr + some_length)

2) не используйте constдля обоих, это работает хорошо:

strf::range((char *)some_char_ptr, (char *)(some_char_ptr + some_length));

Вы можете попробовать заменить (char *)с (const char *)слева или справа, он все еще работает.

tontonCD
источник
Это довольно уродливо, если аргументы указывают на constданные.
Ашеплер
1. Там является не второй аргумент. Я хочу шаблон с одним аргументом. 2. Интересный взлом! -1 за первое предложение и +1 за второе :-P
einpoklum