Запрошено преобразование из лямбда в нескалярный тип
Я создал этот класс, чтобы иметь значение любого типа, которое либо фиксировано, либо пересчитывается каждый раз, когда оно используется:
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(); }
};
Кажется, все следующие выражения работают нормально:
Value<double> a = 2.2;
double b = 1.;
double c = a;
Value<double> d = &b;
Value<int> e = Value<int>([]() { return 1.; });
Но когда я пытаюсь сделать:
Value<double> f = []() { return 1.; };
срабатывает ошибка компиляции:
error: conversion from 'main()::<lambda()>' to non-scalar type 'Value<double>' requested
Вы можете попробовать этот пример здесь .
Почему назначение работает, T
а не работает std::function<T()>
и как сделать так?
Примечание. Мне известен этот ответ, но мне неясно, как исправить проблему без явного вызова конструктора, как это сделал я для Value<double> e
.
Ответы
Почему назначение работает для T, а не для std :: function <T ()> и как я могу это сделать?
Ваш код не использует присваивание, а копирует инициализацию и
Кроме того, неявное преобразование при копировании-инициализации должно производить T непосредственно из инициализатора, в то время как, например, прямая инициализация ожидает неявного преобразования из инициализатора в аргумент конструктора T.
Итак, чтобы заставить его работать, вы должны заставить ваш ctor принимать лямбда напрямую (это упрощенный пример):
template<typename T>
class Value {
std::function<T()> get;
public:
template<class Y>
Value(Y lambda ) : get( std::move( lambda ) ) {}
};
live code Вы, вероятно, захотите добавить ограничение using std::enable_if
или концепцию, если C ++ 20 разрешен для этого ctor, а также в этой форме этот конструктор попытается принять, что другие перегрузки не будут и могут вызывать загадочные ошибки. И в соответствии с этим параметром шаблона enable_if является лямбда (с определенной сигнатурой), это может быть так же просто, как
template<class Y, typename = decltype(std::declval<Y&>()())>
Value(Y lambda ) : get( std::move( lambda ) ) {}
который поддерживает C ++ 14. Вот еще один живой пример, где вы можете видеть, что этот конструктор не используется для инициализатора типа int
:
Value<double> d2 = 123;
prog.cpp: 9: 5: примечание: шаблон кандидата игнорируется: ошибка подстановки [с Y = int]: вызываемый тип объекта 'int' не является функцией или указателем функции Значение (Y лямбда): get (std :: move (lambda )) {}
Лямбда - это не std::function
. Это означает, что когда вы это сделаете
Value<double> f = []() { return 1.; };
вам нужно преобразовать []() { return 1.; }
в a std::function
, определяемое пользователем преобразование, а затем вам необходимо преобразовать это std::function
в Value<double>
другое преобразование, определяемое пользователем. Это два определяемых пользователем преобразования, когда вам разрешено только одно такое преобразование. Вот почему код не компилируется.