Як я можу запобігти відгадуванню 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 була оновлена, щоб обійти цю ситуацію, але питання стоїть як задане.


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

Що про проходження через a using?

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

tfp x = &strf::range;

char const * a = "abcd";

(*x)(a, a+2);

І це компілює? Другий рядок виглядає особливо підозрілим.
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 *)зліва чи справа, він все ще працює.


Це досить негарно, якщо аргументи вказують на constдані.
асчеплер

1. Там є не другий аргумент. Я хочу шаблон одного аргументу. 2. Цікавий хак! -1 для першої пропозиції та +1 для другої :-P
einpoklum
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.