Konversi dari lambda ke jenis non-skalar diminta
Saya membuat kelas ini sehingga saya dapat memiliki nilai jenis apa pun yang diperbaiki atau dihitung ulang setiap kali digunakan:
template<typename T>
class Value {
private:
bool fixed;
union {
T value;
std::function<T()> get;
};
public:
Value(const T& value) : fixed(true), value(value) {}
Value(const std::function<T()>& get) : fixed(false), get(get) {}
Value(const T *pointer) : Value([pointer]() { return *pointer; }) {}
~Value() {}
operator T() { return fixed ? value : get(); }
};
Semua ekspresi berikut tampaknya berfungsi dengan baik:
Value<double> a = 2.2;
double b = 1.;
double c = a;
Value<double> d = &b;
Value<int> e = Value<int>([]() { return 1.; });
Tetapi ketika saya mencoba melakukan:
Value<double> f = []() { return 1.; };
kesalahan kompilasi dipicu:
error: conversion from 'main()::<lambda()>' to non-scalar type 'Value<double>' requested
Anda dapat mencoba contoh ini di sini .
Mengapa penugasan berhasil T
dan tidak, std::function<T()>
dan bagaimana cara membuatnya demikian?
Catatan: Saya mengetahui jawaban ini tetapi saya tidak menjelaskan kepada saya cara memperbaiki masalah tanpa harus secara eksplisit memanggil konstruktor seperti yang saya lakukan Value<double> e
.
Jawaban
Mengapa penetapan berfungsi untuk T dan bukan std :: function <T ()> dan bagaimana cara membuatnya begitu?
Kode Anda tidak menggunakan tugas, tetapi menyalin inisialisasi dan
Selain itu, konversi implisit dalam inisialisasi salinan harus menghasilkan T langsung dari penginisialisasi, sedangkan, misalnya inisialisasi langsung mengharapkan konversi implisit dari penginisialisasi ke argumen konstruktor T.
Jadi untuk membuatnya berfungsi, Anda harus membuat ctor Anda menerima lambda secara langsung (ini adalah contoh yang disederhanakan):
template<typename T>
class Value {
std::function<T()> get;
public:
template<class Y>
Value(Y lambda ) : get( std::move( lambda ) ) {}
};
live code Anda mungkin ingin menambahkan pembatasan menggunakan std::enable_if
atau konsep jika C ++ 20 diizinkan untuk ctor ini serta dalam bentuk ini konstruktor ini akan mencoba untuk menerima setiap kelebihan beban lain tidak akan dan dapat menghasilkan kesalahan samar. Dan menurut parameter template enable_if ini adalah lambda (dengan tanda tangan tertentu), ini bisa sesederhana
template<class Y, typename = decltype(std::declval<Y&>()())>
Value(Y lambda ) : get( std::move( lambda ) ) {}
yang mendukung C ++ 14. Berikut adalah contoh langsung lainnya di mana Anda dapat melihat bahwa konstruktor ini tidak digunakan untuk penginisialisasi tipe int
:
Value<double> d2 = 123;
prog.cpp: 9: 5: catatan: template kandidat diabaikan: kegagalan substitusi [dengan Y = int]: disebut tipe objek 'int' bukan fungsi atau penunjuk fungsi Nilai (Y lambda): get (std :: move (lambda )) {}
Lambda bukanlah std::function
. Itu artinya jika Anda melakukannya
Value<double> f = []() { return 1.; };
Anda perlu mengubahnya []() { return 1.; }
menjadi std::function
, yang merupakan konversi yang ditentukan pengguna, dan kemudian Anda perlu mengubahnya std::function
menjadi Value<double>
, yang merupakan konversi buatan pengguna lainnya. Itu adalah dua konversi yang ditentukan pengguna bila Anda hanya diizinkan untuk satu konversi semacam itu. Inilah sebabnya mengapa kode gagal untuk dikompilasi.