Bagaimana cara membatasi tipe paket parameter di c++11? Dan Bagaimana cara mengimplementasikan template di cpp?

Untuk pertanyaan pertama:

Saya ingin menulis fungsi untuk menggabungkan string, dan dapat menerima banyak string;

#include <string>
#include <vector>
#include <type_traits>

template <class... Args, typename std::enable_if<std::is_same<typename std::decay<Args...>::type, std::string>::type>::type>
std::string foo(const std::string &first, const Args &... senconds) {
    std::string delimiter = "$$";
    std::string ret = first;
    std::vector<std::string> vec{senconds...};
    for (auto second = vec.rbegin(); second != vec.rend(); second++) {
        ret = delimiter + *second + delimiter + ret;
    }
    return ret;
}

tetapi ketika saya memintanya seperti:

std::string name = "x";
name = foo(name, "xxx");

kompiler akan membuat kesalahan:

error: no matching function for call to ‘foo(std::__cxx11::string&, const char [4])’

dan akan ada beberapa catatan:

note: couldn't deduce template parameter ‘<anonymous>’

Saya rasa saya harus mengubah batasan dalam templat, dan saya telah mencoba semua metode terkait di type_traits, tetapi tidak ada satupun yang berhasil.

Untuk pertanyaan kedua:

Saya ingin menyembunyikan implementasi beberapa fungsi, tetapi untuk fungsi templat, ia tidak dapat memasukkan definisi ke dalam .hpp, dan memasukkan implementasi ke dalam .cpp, kompiler akan memunculkan kesalahan undefined reference. Apakah ada cara elegan untuk mengatasi ini?

Terima kasih.


person Patrick    schedule 28.11.2019    source sumber
comment
Untuk pertanyaan kedua, silakan lihat stackoverflow.com/questions/495021/ (dan jangan berkecil hati dengan judul pertanyaan). Namun mohon juga jangan menanyakan dua pertanyaan terpisah sekaligus.   -  person Max Langhof    schedule 28.11.2019
comment
Selain itu: Anda tidak perlu setiap elemen Args... menjadi std::string, hanya sesuatu yang dapat Anda contohkan std::string   -  person Caleth    schedule 28.11.2019
comment
@MaxLanghof, saya akan membagi dua pertanyaan terpisah lain kali.   -  person Patrick    schedule 29.11.2019


Jawaban (2)


Ada sedikit yang perlu diungkap di sini.

  • std::decay<Args...>::type tidak bisa bekerja. std::decay hanya mengambil satu argumen templat, tetapi Anda mencoba memperluas paketnya di sini. Perluasan perlu dilakukan pada is_same.

  • Anda juga kehilangan cara untuk menggabungkan semua predikat is_same. Apakah Anda ingin and semuanya atau or semuanya? Agaknya and. Di C++17 hal itu mudah dilakukan dengan ekspresi lipat, tetapi untuk C++11 kita harus bekerja sedikit.

  • Akhirnya hal yang dikeluhkan kompiler: std::enable_if<bla>::type dievaluasi menjadi void jika bla adalah true. Itu berarti Anda secara resmi mengharapkan argumen templat non-tipe, dan kompiler mengeluh karena tidak dapat menyimpulkan nilai tipe void mana yang harus disimpulkan. Hal ini biasanya diatasi dengan membentuk pointer ke sana dan menetapkannya secara default ke nullptr: std::enable_if<bla>::type* = nullptr.

  • Tampaknya (?) yang Anda harapkan foo(someString, "stringLiteral"); berfungsi. Tidak akan terjadi, karena string literal bukan std::string. Mungkin Anda menginginkan predikat yang berbeda, namun untuk jawaban kali ini saya tetap pada kondisi awal.


Menggabungkan semua itu:

  • Di C++17, Anda akan menulis

    template <class... Args,
        std::enable_if_t<
                (std::is_same_v<std::decay_t<Args>, std::string> && ...)
            >* = nullptr
        >
    

    https://godbolt.org/z/84Dcmt

  • Di C++11, kami menggunakan helper ini dan menambahkan kembali typename dan ::type verbositas:

    template <class... Args,
        typename std::enable_if<
            var_and<
                std::is_same<typename std::decay<Args>::type, std::string>::value...
                >::value
            >::type* = nullptr
        >
    

    https://godbolt.org/z/2eFyX7

person Max Langhof    schedule 28.11.2019
comment
Saya sudah mencoba demo c++11, tetapi jika foo(..) dipanggil seperti foo(name, xxx), masih tidak dapat menemukan implementasi template yang benar. - person Patrick; 29.11.2019
comment
<source>:36:12: error: no matching function for call to 'foo' name = foo(name, stringRef, name, stringRef, "xxx"); Saya telah memodifikasi kode seperti itu. - person Patrick; 29.11.2019
comment
Dan ketika saya menerapkan kode ini ke proyek saya, ada kesalahan: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’ - person Patrick; 29.11.2019
comment
@Patrick Seperti yang saya katakan, dengan is_same Anda tidak akan dapat meneruskan string literal (lihat poin terakhir). Dan kesalahannya mungkin karena Anda mencoba menggunakannya secara tidak benar, tetapi saya tidak dapat mendiagnosisnya tanpa melihat apa yang Anda lakukan. Tapi saya melihat Anda menemukan sesuatu yang cocok untuk Anda, jadi itu bagus. - person Max Langhof; 29.11.2019

Berdasarkan jawaban MaxLanghof, saya mengubah template menjadi:

template <class... Args,
    typename std::enable_if<var_and<std::is_constructible<
        std::string, Args>::value...>::value>::type * = nullptr>

Dalam bentuk ini, fungsi foo dapat dipanggil seperti name = foo(name, stringRed, "xxx").

Terima kasih @MaxLanghof lagi.

person Patrick    schedule 29.11.2019