Bagaimana saya bisa mencegah C ++ menebak argumen templat kedua?

26

Saya menggunakan pustaka C ++ ( strf ) yang, di suatu tempat di dalamnya, memiliki kode berikut:

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) { /* ... */ }
}

Sekarang, saya ingin menggunakan strf::range<const char*>(some_char_ptr, some_char_ptr + some_length)kode saya. Tetapi jika saya melakukannya, saya mendapatkan kesalahan berikut (dengan 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)

The perpustakaan kode mungkin dapat diubah untuk menghindari ini (misalnya menggunakan:

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)

untuk memastikan Rangebukan pointer); tapi saya tidak bisa melakukan perubahan itu sekarang. Sebagai gantinya, saya ingin menunjukkan ke kompiler bahwa saya benar-benar bermaksud hanya memiliki satu argumen templat, bukan satu yang ditentukan dan satu lagi dideduksi.

Bisakah saya melakukan itu?

Akan menghargai jawaban untuk C ++ 11 dan C ++ 14; C ++ 17 jawaban yang melibatkan panduan deduksi kurang relevan tetapi jika Anda punya, silakan posting (untuk versi NVCC mendatang ...)


Pembaruan: Perpustakaan strf itu sendiri telah diperbarui untuk menghindari situasi ini, tetapi pertanyaannya tetap seperti yang ditanyakan.

einpoklum
sumber
1
Saya menduga melewati iterator khusus yang membungkus tipis char*tetapi bukankah itu bukan solusi?
Konrad Rudolph
1
@KonradRudolph: Itu solusinya, tetapi tidak menjawab pertanyaan saya. Saya sebenarnya sudah memiliki solusi lain (khusus untuk apa yang ada di /*...*/), tapi saya ingin mengambil jalan besar di sini.
einpoklum
1
Dalam hal ini, jawaban saya (duga) adalah "tidak dapat dilakukan", sayangnya. Agar adil, saya tidak yakin saya akan menerima solusi yang disarankan dalam kode saya sendiri.
Konrad Rudolph
Hanya untuk memperjelas: Apakah Anda menginginkan solusi umum, yang akan selalu berfungsi untuk membedakan panggilan antara kelebihan template dengan satu vs dua parameter atau apakah Anda hanya menginginkan solusi khusus untuk kasus ini?
walnut
@walnut: Solusi umum akan lebih baik; Skenario spesifik saya kebanyakan adalah motivasi untuk masalah tersebut.
einpoklum

Jawaban:

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);
}

Kemudian panggil range1alih-alih strf::range.

range1_ptr<T>(...)selalu dapat digunakan untuk secara eksplisit memanggil templat yang mengambil satu argumen templat, tetapi tidak melakukan deduksi dari argumen. range1mereplikasi deduksi dari strf::rangetemplat asli .

Ini berfungsi, karena [temp.deduct.funcaddr] / 1 mengatakan bahwa pengurangan argumen templat ketika mengambil alamat suatu fungsi tanpa tipe target konversi dilakukan pada setiap templat fungsi kandidat seolah-olah parameter dan daftar argumen dari panggilan hipotetis adalah kosong. Jadi argumen templat kedua tidak dapat disimpulkan untuk kelebihan kedua dengan dua parameter templat. Satu-satunya kandidat yang tersisa adalah kelebihan beban pertama, yang akan dipilih sebagai target dari pointer fungsi.

Selama tidak ada templat fungsi kandidat kedua yang template-id yang valid dengan hanya satu argumen dapat dibentuk, range1_ptrselalu dapat digunakan untuk memanggil templat fungsi mengambil satu argumen dengan jelas. Kalau tidak, instantiasi range1_ptrakan memberikan kesalahan karena ambiguitas.

kenari
sumber
Tidak akan ada ambiguitas tentang strf::range<T>?
einpoklum
1
@einpoklum Mengkompilasi dengan baik pada GCC dan Dentang. Saya tidak memeriksa standar, tetapi akan terkejut jika itu seharusnya ambigu.
walnut
Mungkin Anda harus mengubah nama fungsinya pretty_please_with_sugar_on_top()? ... C ++ kadang-kadang bisa sangat aneh ...
einpoklum
Anda berhasil melepaskan jawaban yang diterima :-P
einpoklum
11

Bagaimana dengan melewati sebuah using?

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

tfp x = &strf::range;

char const * a = "abcd";

(*x)(a, a+2);
maks66
sumber
Dan ini mengkompilasi? Baris kedua terlihat sangat mencurigakan.
einpoklum
@einpoklum - lucu, bukan?
maksimal 66
@einpoklum - sayangnya bukan solusi umum; berfungsi dalam hal ini karena (jika saya tidak salah) hanya range()versi pertama yang kompatibel dengan tpf; kasus lain bisa berbeda.
maksimal 66
@einpoklum - di baris kedua Anda juga dapat menjelaskan parameter templat ( tfp x = &strf::range<char const *>;); dengan cara ini saya kira Anda memiliki solusi umum, hampir setara dengan kacang walnut
max66
0

Sebuah solusi adalah

1) pertama-tama, Anda harus menentukan tipe untuk argumen kedua, misalnya (char *)(some_char_ptr + some_length)

2) tidak digunakan constuntuk keduanya, ini bekerja dengan baik:

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

Anda dapat mencoba menggantinya (char *)dengan (const char *)di kiri ATAU di kanan, itu masih berfungsi.

tontonCD
sumber
Ini sangat jelek jika argumen menunjuk pada constdata.
aschepler
1. Tidak ada argumen kedua. Saya ingin template argumen tunggal. 2. Retas yang menarik! -1 untuk saran pertama dan +1 untuk yang kedua :-P
einpoklum