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.


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.


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

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.


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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.