簡體   English   中英

給定工廠 object 和可變參數 arguments 列表,是否可以就地初始化 std::variant ?

[英]Is it possible to in-place initailize std::variant given a factory object and variadic arguments list?

std::variant有一個構造函數,它接受std::in_place_t<Type>, Args &&... arguments 導致就地構造。

    {
        std::cout << "in_place_type:\n";
        const auto var = std::variant<callable>(std::in_place_type<callable>);
        std::cout << "finished\n";
    }

我想知道是否有可能在給定工廠 object 的情況下就地構造:

    {
        std::cout << "by factory:\n";
        const auto var = std::variant<callable>{[] ()-> callable {return {}; }()};
        std::cout << "finished\n";
    }

https://godbolt.org/z/rY5WG1hGn

#include <variant>

#include <iostream>

int main(int, char **)
{
    struct callable
    {
        callable()
        {
            std::cout << "callable()" << std::endl;
        }

        ~callable()
        {
            std::cout << "~callable()" << std::endl;
        }
    };

    {
        std::cout << "in_place_type:\n";
        const auto var = std::variant<callable>(std::in_place_type<callable>);
        std::cout << "finished\n";
    }
    std::cout << '\n';
        
    {
        std::cout << "by factory:\n";
        const auto var = std::variant<callable>{[] ()-> callable {return {}; }()};
        std::cout << "finished\n";
    }
}

我認為在這種情況下它不起作用,因為工廠使用了std::variant(T &&t)構造函數,並且它不能使用復制省略。


我試圖詳細說明HTNW的答案(從參數創建一個 onbject),但得到一個編譯器錯誤:

#include <variant>
#include <tuple>

struct to_construct {
  // must NOT exist
  // template<typename F>
  // to_construct(initializer<F>) { std::cout << "Foiled!\n"; }
  // or (more likely)
  // template<typename T>
  // to_construct(T) { std::cout << "Foiled again!\n"; }
  to_construct() = default;
  to_construct(to_construct&&) = delete;
  to_construct(to_construct const&) = delete;
};

#include <iostream>

struct callable
{
    callable(int i)
    {
        std::cout << "callable():" << i << std::endl;
    }

    ~callable()
    {
        std::cout << "~callable()" << std::endl;
    }
};

template<typename T>
struct box {
  T x;
  template<typename F>
  box(F f) 
  : x(f())
  {}
};

template<typename F>
struct initializer {
  F init;
  operator auto() {
    return init();
  }
};

template<typename F>
initializer(F) -> initializer<F>;

template <typename ... Types, typename Factory, typename ... Args>
std::variant<box<Types>...> variant_from(Factory &&f, Args &&... args)
{
    return {
        initializer{
            [tupleArgs = std::forward_as_tuple(args...),
            f = std::forward<Factory>(f)](){
                return std::apply(f, tupleArgs);
            }
        }
    };
}


int main() 
{  
  {
      const auto var = 
        std::variant<to_construct>(initializer{
            []() -> to_construct { 
                std::cout << "Success\n";
                return {}; 
                }
            });
  }
   
  {
      const auto var = variant_from<callable>([](int i) { return callable(i);}, 42);
  }
}
<source>: In instantiation of 'box<T>::box(F) [with F = initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >; T = callable]':
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:283:4:   required from 'constexpr std::__detail::__variant::_Uninitialized<_Type, false>::_Uninitialized(std::in_place_index_t<0>, _Args&& ...) [with _Args = {initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >}; _Type = box<callable>]'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:385:4:   required from 'constexpr std::__detail::__variant::_Variadic_union<_First, _Rest ...>::_Variadic_union(std::in_place_index_t<0>, _Args&& ...) [with _Args = {initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >}; _First = box<callable>; _Rest = {}]'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:460:4:   required from 'constexpr std::__detail::__variant::_Variant_storage<false, _Types ...>::_Variant_storage(std::in_place_index_t<_Np>, _Args&& ...) [with long unsigned int _Np = 0; _Args = {initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >}; _Types = {box<callable>}]'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:557:20:   required from 'constexpr std::__detail::__variant::_Variant_base<_Types>::_Variant_base(std::in_place_index_t<_Np>, _Args&& ...) [with long unsigned int _Np = 0; _Args = {initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >}; _Types = {box<callable>}]'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:1448:57:   required from 'constexpr std::variant<_Types>::variant(std::in_place_index_t<_Np>, _Args&& ...) [with long unsigned int _Np = 0; _Args = {initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >}; _Tp = box<callable>; <template-parameter-2-4> = void; _Types = {box<callable>}]'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:1419:27:   required from 'constexpr std::variant<_Types>::variant(_Tp&&) [with _Tp = initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >; <template-parameter-2-2> = void; <template-parameter-2-3> = void; _Tj = box<callable>; <template-parameter-2-5> = void; _Types = {box<callable>}]'
<source>:61:5:   required from 'std::variant<box<Types>...> variant_from(Factory&&, Args&& ...) [with Types = {callable}; Factory = main()::<lambda(int)>; Args = {int}]'
<source>:78:46:   required from here
<source>:36:8: error: no match for call to '(initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >) ()'
   36 |   : x(f())
      |       ~^~
ASM generation compiler returned: 1
<source>: In instantiation of 'box<T>::box(F) [with F = initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >; T = callable]':
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:283:4:   required from 'constexpr std::__detail::__variant::_Uninitialized<_Type, false>::_Uninitialized(std::in_place_index_t<0>, _Args&& ...) [with _Args = {initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >}; _Type = box<callable>]'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:385:4:   required from 'constexpr std::__detail::__variant::_Variadic_union<_First, _Rest ...>::_Variadic_union(std::in_place_index_t<0>, _Args&& ...) [with _Args = {initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >}; _First = box<callable>; _Rest = {}]'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:460:4:   required from 'constexpr std::__detail::__variant::_Variant_storage<false, _Types ...>::_Variant_storage(std::in_place_index_t<_Np>, _Args&& ...) [with long unsigned int _Np = 0; _Args = {initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >}; _Types = {box<callable>}]'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:557:20:   required from 'constexpr std::__detail::__variant::_Variant_base<_Types>::_Variant_base(std::in_place_index_t<_Np>, _Args&& ...) [with long unsigned int _Np = 0; _Args = {initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >}; _Types = {box<callable>}]'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:1448:57:   required from 'constexpr std::variant<_Types>::variant(std::in_place_index_t<_Np>, _Args&& ...) [with long unsigned int _Np = 0; _Args = {initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >}; _Tp = box<callable>; <template-parameter-2-4> = void; _Types = {box<callable>}]'
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/variant:1419:27:   required from 'constexpr std::variant<_Types>::variant(_Tp&&) [with _Tp = initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >; <template-parameter-2-2> = void; <template-parameter-2-3> = void; _Tj = box<callable>; <template-parameter-2-5> = void; _Types = {box<callable>}]'
<source>:61:5:   required from 'std::variant<box<Types>...> variant_from(Factory&&, Args&& ...) [with Types = {callable}; Factory = main()::<lambda(int)>; Args = {int}]'
<source>:78:46:   required from here
<source>:36:8: error: no match for call to '(initializer<variant_from<callable, main()::<lambda(int)>, int>(main()::<lambda(int)>&&, int&&)::<lambda()> >) ()'
   36 |   : x(f())
      |       ~^~
Execution build compiler returned: 1

https://godbolt.org/z/8MMMEe3jx

是的。

將工廠包裝在一個轉換為包含類型的類型中,並且包含類型不需要具有優先的構造函數。

template<typename F>
struct initializer {
  F init;
  operator auto() {
    return init();
  }
};
template<typename F>
initializer(F) -> initializer<F>;

struct to_construct {
  // must NOT exist
  // template<typename F>
  // to_construct(initializer<F>) { std::cout << "Foiled!\n"; }
  // or (more likely)
  // template<typename T>
  // to_construct(T) { std::cout << "Foiled again!\n"; }
  to_construct() = default;
  to_construct(to_construct&&) = delete;
  to_construct(to_construct const&) = delete;
};

int main() {
  std::variant<to_construct, int> v;
  v.emplace<to_construct>(initializer{[]() -> to_construct { std::cout << "Success\n"; return {}; }});
}

包含的類型不能有一個構造函數匹配initializer這一點很重要:這意味着你不能安全地對你無法控制的類型的variant執行這個技巧。 如果您需要以這種方式在variant中構造一些外部T ,請將T包裝在您自己的控件的box ,在這種情況下,您可以直接從工廠添加構造函數。

// do NOT do this, because it might fail silently and weirdly
// template<typename T>
// void foo() {
//   std::variant<int, T> v;
//   v.emplace<1>(initializer{[]() -> T { return {}; }});
// }

template<typename T>
struct box {
  T x;
  template<typename F>
  box(F f) : x(f()) { }
};
template<typename T>
void foo() {
  std::variant<int, box<T>> v;
  v.template emplace<1>([]() -> T { return {}; });
}

即,您可以在工廠的variant中構建 object有時無需更改變variant類型(當您控制備選方案時),但通常您可能需要將一些備選方案更改為box


基於上述內容的擴展代碼的問題是您嘗試將initializerbox一起使用。 box不需要initializer 它直接采用 lambdas。 此外,由於初始化 lambda 不必超過variant的構造,它幾乎不應該按值捕獲。

// it is convention in the C++ standard libraries to not bother forwarding functors
template<typename... Types, typename Factory, typename... Args>
std::variant<box<Types>...> variant_from(Factory f, Args&&... args) {
    return [&]() { return f(std::forward<Args>(args)...); };
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM