Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/153.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;11工厂函数返回元组的模式_C++_C++11_Factory - Fatal编程技术网

C++ C++;11工厂函数返回元组的模式

C++ C++;11工厂函数返回元组的模式,c++,c++11,factory,C++,C++11,Factory,在我的项目中,我有一些功能,如 std::tuple<VAO, Mesh, ShaderProgram> LoadWavefront(std::string filename); 问题是,这要求这些类中的每个类都有一个默认构造函数,该构造函数在无效状态下创建它们,这很容易出错。我如何在不必std::获取每一项的情况下解决这个问题?有一种优雅的方法可以做到这一点吗?您可以使用boost::optional: boost::optional<VAO> teapotVAO;

在我的项目中,我有一些功能,如

std::tuple<VAO, Mesh, ShaderProgram> LoadWavefront(std::string filename);

问题是,这要求这些类中的每个类都有一个默认构造函数,该构造函数在无效状态下创建它们,这很容易出错。我如何在不必
std::获取
每一项的情况下解决这个问题?有一种优雅的方法可以做到这一点吗?

您可以使用
boost::optional

boost::optional<VAO> teapotVAO;
boost::optional<Mesh> teapotMesh;
boost::optional<ShaderProgram> teapotShader;
std::tie(teapotVAO, teapotMesh, teapotShader)
    = LoadWavefront("assets/teapot.obj");
boost::可选的teapotVAO;
boost::可选茶壶网;
boost::可选的teapotShader;
标准::tie(teapotVAO、teapotMesh、teapotShader)
=加载波前(“assets/teapot.obj”);

当然,您必须更改访问这些值的方式,以便始终执行
*teapotVAO
,但如果您破坏了任何访问,至少编译器会让您知道

让我们更进一步,假设这些类没有默认构造函数

一种选择是这样的:

VAO teapotVAO;
Mesh teapotMesh;
ShaderProgram teapotShader;
std::tie(teapotVAO, teapotMesh, teapotShader)
    = LoadWavefront("assets/teapot.obj");
auto tup = LoadWavefront("assets/teapot.obj");
VAO teapotVAO(std::move(std::get<0>(tup)));
Mesh teapotMesh(std::move(std::get<1>(tup)));
ShaderProgram teapotShader(std::move(std::get<2>(tup)));
auto-tup=LoadWavefront(“assets/teapot.obj”);
VAO teapotVAO(std::move(std::get(tup));
网格teapotMesh(std::move(std::get(tup));
着色器程序teapotShader(std::move(std::get(tup));
这仍然使tup成为一个大部分已清理的对象,这并不理想

但是等等…为什么这些人甚至需要拥有所有权

auto tup = LoadWavefront("assets/teapot.obj");
VAO& teapotVAO=std::get<0>(tup);
Mesh& teapotMesh=std::get<1>(tup);
ShaderProgram& teapotShader=std::get<2>(tup);
auto-tup=LoadWavefront(“assets/teapot.obj”);
VAO&teapotVAO=std::get(tup);
Mesh&teapotMesh=std::get(tup);
ShaderProgram&teapotShader=std::get(tup);
只要引用与返回的元组在同一范围内,这里就没有问题

就个人而言,这似乎是一个明确的地方,人们应该使用一套智能指针,而不是胡说八道:

LoadWavefront(const char*,std::unique_ptr<VAO>&,std::unique_ptr<Mesh>&,std::unique_ptr<ShaderProgram>&);

std::unique_ptr<VAO> teapotVAO;
std::unique_ptr<Mesh> teapotMesh;
std::unique_ptr<ShaderProgram> teapotShader;
LoadWavefront("assets/teapot.obj",teapotVAO,teapotMesh,teapotShader);
LoadWavefront(const char*,std::unique_ptr&,std::unique_ptr&,std::unique_ptr&);
std::独特的teapotVAO;
std::独特的ptr茶壶网;
std::unique_ptr teapotShader;
LoadWavefront(“assets/teapot.obj”、teapotVAO、teapotMesh、teapotShader);
这将解决所有权问题,并允许一个合理的空状态

编辑:/u/dyp指出,您可以将以下内容与原始输出样式一起使用

std::unique_ptr<VAO> teapotVAO;
std::unique_ptr<Mesh> teapotMesh;
std::unique_ptr<ShaderProgram> teapotShader;
std::tie(teapotVAO,teapotMesh,teapotShader) = LoadWavefront("assets/teapot.obj");
std::unique_ptr teapotVAO;
std::独特的ptr茶壶网;
std::unique_ptr teapotShader;
std::tie(teapotVAO、teapotMesh、teapotShader)=加载波前(“assets/teapot.obj”);

有一种反向控制流样式可能很有用

LoadWavefront("assets/teapot.obj", [&]( VAO teapotVAO, Mesh teapotMesh, ShaderProgram teapotShader ){
  // code
});
使用
VAO&
reference样式,而不是可选样式。在这种情况下,lambda的返回值可以用作
LoadWavefront
的返回值,默认lambda只转发所有3个参数,如果需要,允许“旧式”访问。如果你只想要一个,或者想在加载后做一些事情,你也可以这样做

现在,
LoadWavefront
可能会返回一个
future
,因为它是一个IO函数。在这种情况下,
元组的
未来
。我们可以使上述模式更通用一些:

template<class... Ts, class F>
auto unpack( std::tuple<Ts...>&& tup, F&& f ); // magic
unpack
还可以学习
std::future
s,并自动创建结果的未来

这可能会导致一些恼人的括号级别。如果我们想变得疯狂,我们可以从函数式编程中窃取一页:

LoadWavefront("assets/teapot.obj")
*sync_next* [&]( VAO teapotVAO, Mesh teapotMesh, ShaderProgram teapotShader ){
  // code
};
其中
LoadWavefront
返回
std::future
。指定的操作员
*sync\u next*
在左侧接受一个
std::future
,在右侧接受一个lambda,协商呼叫约定(首先尝试展平
元组
s),并将
future
作为延迟呼叫继续。(请注意,在windows上,
async
返回的
std::future
在销毁时无法
.wait()
,这违反了标准)

然而,这是一种疯狂的做法。可能会有更多类似这样的代码使用建议的
await
类型,但它将提供更清晰的语法来处理它


无论如何,这里是中缀
*then*
命名运算符的完整实现,因为

我觉得很可爱

我如何在不必获取每一项的情况下绕过它?有没有一种优雅的方法可以做到这一点

按值返回,而不是按“值”返回(这是std::tuple允许您执行的操作)

API更改:

class Wavefront
{
public:
    Wavefront(VAO v, Mesh m, ShaderProgram sp); // use whatever construction
                                                // suits you here; you will
                                                // only use it internally
                                                // in the load function, anyway
    const VAO& vao() const;
    const Mesh& mesh() const;
    const ShaderProgram& shader() const;
};

Wavefront LoadWavefront(std::string filename);

我想你的意思是“有一个默认的构造函数在有效状态下创建它们”?至于
std::get
,您是想完全避免这种情况,还是也可以使用一些最小化使用次数的方法?我的建议是:不要使用
元组,而是使用自定义类型。似乎您没有进行一般性编程,因此没有理由使用不为其成员指定合适名称的类型。@不幸的是,有时这些类型没有合理的默认构造函数。@dyp这实际上很有意义。然后,我没有工厂函数,而是有一个构造函数,而不是
teapotMesh
,我有
teapot.Mesh
,所有漂亮的清理/引用位置都是免费的。我没有使用参数,您可以将OP的样式与
unique\u ptr
s一起使用,并以元组形式返回它们。@dyp-oh-yeah-rhr允许这样做。意志edit@dyphrm…是否知道这可能是RVO?我们无法看到此代码中是否发生RVO,因为没有
返回
;)不能忽略作为
LoadWavefront
返回值创建的(可能是)临时值,因为此行不从中构造对象(它仅分配给元组)。但是我们这里不需要RVO,这行所做的只是复制三个指针,然后将源归零。@dyp完全同意这里不需要RVO…如果不是移动,unique_ptr不会让它发生,所以最坏的情况是你将有6个指针副本(3个指针返回,3个指针值)。RVO在原始问题(可能?)的情况下会更有用,但谁知道呢。老问题的有趣解决方案!同意,功能性技术很疯狂:)我真的很喜欢这个主意
#include <utility>
#include <tuple>
#include <iostream>
#include <future>

// a better std::result_of:
template<class Sig,class=void>
struct invoke_result {};
template<class F, class... Args>
struct invoke_result<F(Args...), decltype(void(std::declval<F>()(std::declval<Args>()...)))>
{
  using type = decltype(std::declval<F>()(std::declval<Args>()...));
};
template<class Sig>
using invoke_t = typename invoke_result<Sig>::type;

// complete named operator library in about a dozen lines of code:
namespace named_operator {
  template<class D>struct make_operator{};

  template<class T, class O> struct half_apply { T&& lhs; };

  template<class Lhs, class Op>
  half_apply<Lhs, Op> operator*( Lhs&& lhs, make_operator<Op> ) {
      return {std::forward<Lhs>(lhs)};
  }

  template<class Lhs, class Op, class Rhs>
  auto operator*( half_apply<Lhs, Op>&& lhs, Rhs&& rhs )
  -> decltype( invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
  {
      return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
  }
}

// create a named operator then:
static struct then_t:named_operator::make_operator<then_t> {} then;

namespace details {
  template<size_t...Is, class Tup, class F>
  auto invoke_helper( std::index_sequence<Is...>, Tup&& tup, F&& f )
  -> invoke_t<F(typename std::tuple_element<Is,Tup>::type...)>
  {
      return std::forward<F>(f)( std::get<Is>(std::forward<Tup>(tup))... );
  }
}

// first overload of A *then* B handles tuple and tuple-like return values:
template<class Tup, class F>
auto invoke( Tup&& tup, then_t, F&& f )
-> decltype( details::invoke_helper( std::make_index_sequence< std::tuple_size<std::decay_t<Tup>>{} >{}, std::forward<Tup>(tup), std::forward<F>(f) ) )
{
  return details::invoke_helper( std::make_index_sequence< std::tuple_size<std::decay_t<Tup>>{} >{}, std::forward<Tup>(tup), std::forward<F>(f) );
}

// second overload of A *then* B
// only applies if above does not:
template<class T, class F>
auto invoke( T&& t, then_t, F&& f, ... )
-> invoke_t< F(T) >
{
  return std::forward<F>(f)(std::forward<T>(t));
}
// support for std::future *then* lambda, optional really.
// note it is defined recursively, so a std::future< std::tuple >
// will auto-unpack into a multi-argument lambda:
template<class X, class F>
auto invoke( std::future<X> x, then_t, F&& f )
-> std::future< decltype( std::move(x).get() *then* std::declval<F>() ) >
{
  return std::async( std::launch::async,
    [x = std::move(x), f = std::forward<F>(f)]() mutable {
      return std::move(x).get() *then* std::move(f);
    }
  );
}

int main()
{
  7
  *then* [](int x){ std::cout << x << "\n"; };

  std::make_tuple( 3, 2 )
  *then* [](int x, int y){ std::cout << x << "," << y << "\n"; };

  std::future<void> later =
    std::async( std::launch::async, []{ return 42; } )
    *then* [](int x){ return x/2; }
    *then* [](int x){ std::cout << x << "\n"; };
  later.wait();
}
LoadWaveFront("assets/teapot.obj")
*then* [&]( VAO teapotVAO, Mesh teapotMesh, ShaderProgram teapotShader ){
  // code
}
class Wavefront
{
public:
    Wavefront(VAO v, Mesh m, ShaderProgram sp); // use whatever construction
                                                // suits you here; you will
                                                // only use it internally
                                                // in the load function, anyway
    const VAO& vao() const;
    const Mesh& mesh() const;
    const ShaderProgram& shader() const;
};

Wavefront LoadWavefront(std::string filename);