Ini adalah tindak lanjut dari pertanyaan saya diberikan di sini. Pada akhirnya saya ingin membuat constexpr std::array yang berisi teks dengan indeks berjalan yang ditambahkan.

Saya ingin mencoba pendekatan yang berbeda dari pertanyaan sebelumnya.

Hampir semuanya, apa yang saya lakukan dalam kode di bawah ini adalah constexpr. Tapi mungkin, itu hanya masalah lama mengembalikan pointer ke variabel yang sudah tidak ada lagi. Tapi, saya meragukan ini.

Silakan lihat kode berikut, di mana garis yang tidak berfungsi dalam fungsi utama ditandai.

#include <iostream>
#include <algorithm>
#include <iterator>
#include <array>
#include <string>

// Some example text
static constexpr const char BaseString[]{ "text" };

// To create something like "text123" as constexpr
template <const size_t numberToConvert, const char* Text>
class Converter {
public:
    // Some helper variables
    static constexpr size_t TextLength{ std::char_traits<char>::length(Text) };
    static constexpr size_t NumberOfDigits{ ([]() constexpr noexcept {size_t result = 0; int temp = numberToConvert; for (; temp != 0; temp /= 10) ++result; return result; }()) };
    static constexpr size_t ArrayLength{ (numberToConvert ? 1u : 2u) + NumberOfDigits + TextLength };

    // Here we will build the text
    char buf[ArrayLength]{};

    // Constructor: Convert number to character digits
    constexpr Converter() noexcept {
        size_t i{ 0 };  for (; i < TextLength; ++i) buf[i] = Text[i]; // Copy text
        if (numberToConvert == 0) buf[i] = '0';     
        else {
            i = NumberOfDigits + TextLength - 1;    // Convert number to character digits
            int number = numberToConvert; for (; number; number /= 10)
                buf[i--] = number % 10 + '0';
        }
    }
    // cast operator
    constexpr operator const char* () const noexcept { return buf; }
    // For test purposes
    constexpr const char* data() const noexcept { return buf; }
};

// Driver program
int main() {

    // Temporaray constexprs
    constexpr Converter<123, BaseString> conv123{};     // Default construction
    constexpr auto conv2 = Converter<2, BaseString>();  // Assign / copy

    // Build constexpr std::array and initialize it with constexprs
    constexpr std::array< const char*, 2> convArray1{ conv123, conv2 };
    // Show that it works
    std::copy(convArray1.begin(), convArray1.end(), std::ostream_iterator<const char*>(std::cout, "\n"));

    // Does compile, but not work. Array will be initialized with nullptr *******************************************
    constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };
    std::cout << convArray2[0] << '\n' << convArray2[0] << '\n';

    return 0;
}

Jadi, saya dapat membuat constexpr "nilai" dengan kelas template saya. Nilai tersebut dapat digunakan dalam daftar "penginisialisasi" untuk constexpr std::array. Tetapi, jika saya ingin menggunakan kelas saya secara langsung di daftar penginisialisasi, maka itu dikompilasi, tetapi hanya menyimpan nullptrs. Keluaran dari program ini adalah:

text123
text2
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7
╠╠╠╠╠╠╠╠╠╠╠╠╠╠<½7

Mengapa ini terjadi? Atau, apakah ada solusi?


Dikompilasi dengan Microsoft Visual Studio Community 2019, Versi 16.8.2, C++17, Debug, X86

1
Armin Montigny 13 Januari 2021, 22:54

3 jawaban

Jawaban Terbaik

Kode Anda menghasilkan waktu kompilasi yang menggantung pointer (yang seharusnya tidak mungkin) di MSVC.

Memperbaiki:

template <const size_t numberToConvert, const char* Text>
class Converter {
  // blah
  std::array<char, ArrayLength> buf{};
  constexpr operator std::array<char, ArrayLength>() const { return buf; }
  constexpr std::array<char, ArrayLength> get() const { return *this; }
};

Dan hapus operator konversi dan metode data lainnya.

template<const size_t numberToConvert, const char* Text>
constexpr auto Converted = Converter<numberToConvert, Text>{}.get();

Dan sekarang gunakan Converted<blah...>.data() untuk mendapatkan pointer yang Anda inginkan.

Jika Anda benar-benar ingin konversi implisit ke penunjuk karakter:

template<const size_t numberToConvert, const char* Text>
struct Convertest {
  constexpr operator char const*() const { return Converted<numberToConvert,Text>.data(); }
};

Ganti nama kelas dan variabel sesuka Anda.

2
Yakk - Adam Nevraumont 15 Januari 2021, 18:34
constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), 
                                                  Converter<2, BaseString>().data() };

Di sini, Anda menyimpan pointer ke variabel sementara - kedua objek Converter dianggap ada setelah ;. Membuat dereferensi pointer UB.

Dentang menolak kode tersebut memberikan pesan yang cukup membantu:

<source>:51:43: note: pointer to subobject of temporary is not a constant expression
<source>:51:55: note: temporary created here
    constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };
                                                      ^
2 errors generated.
Execution build compiler returned: 1

Saya tidak yakin tentang aturan constexpr spesifik tetapi kodenya tidak aman meskipun akan dikompilasi.

1
Quimby 13 Januari 2021, 20:17

Di Referensi Cpp Anda dapat melihatnya

Ekspresi konstanta adalah salah satu [...] ekspresi konstanta inti nilai yang nilainya memenuhi batasan berikut: [...] jika nilainya bertipe pointer, ia menyimpan - alamat objek dengan durasi penyimpanan statis

Jadi, untuk convArray1

constexpr std::array< const char*, 2> convArray1{ conv123, conv2 };

Anda harus membuat static conv123 dan conv2

// VVVVVV
   static constexpr Converter<123, BaseString> conv123{};
   static constexpr auto conv2 = Converter<2, BaseString>();
// ^^^^^^

Karena Anda tidak dapat memiliki ekspresi konstan dari pointer dengan penyimpanan tidak statis.

Untuk convArray2

constexpr std::array< const char*, 2> convArray2{ Converter<2, BaseString>(), Converter<2, BaseString>().data() };

Saya tidak melihat cara untuk mendapatkan objek constexpr dari pointer di dalam objek sementara.

1
max66 13 Januari 2021, 20:29