ラムダ型から非スカラー型への変換が要求されました

Dec 08 2020

このクラスを作成したので、使用するたびに固定または再計算される任意のタイプの値を持つことができます。

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

回答

4 Slava Dec 08 2020 at 00:22

std :: function <T()>ではなくTに割り当てが機能するのはなぜですか?また、そのようにするにはどうすればよいですか?

コードは割り当てを使用しませんが、初期化をコピーし、

さらに、コピー初期化での暗黙的な変換では、初期化子から直接Tを生成する必要がありますが、たとえば、直接初期化では、初期化子からTのコンストラクターの引数への暗黙的な変換が必要です。

したがって、それを機能させるには、ctorがラムダを直接受け入れるようにする必要があります(これは単純化された例です)。

template<typename T>
class Value {
    std::function<T()> get;    
public:
    
    template<class Y>
    Value(Y lambda ) : get( std::move( lambda ) )  {}
};

ライブコードstd::enable_ifC ++ 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'は関数または関数ポインターではありませんValue(Y lambda):get(std :: move(lambda) )){}

4 NathanOliver Dec 08 2020 at 00:17

ラムダはではありませんstd::function。それはあなたがするときを意味します

Value<double> f = []() { return 1.; };

あなたは、変換する必要がある[]() { return 1.; }std::functionユーザー定義の変換である、その後、あなたはそれを変換する必要があるstd::functionValue<double>別のユーザー定義の変換です。このような変換が1つまでしか許可されていない場合、これは2つのユーザー定義の変換です。これが、コードのコンパイルに失敗する理由です。