【问题标题】:Union Template with std::variant and std::visit带有 std::variant 和 std::visit 的联合模板
【发布时间】:2020-08-21 11:59:28
【问题描述】:

我正在使用一个模板库,因为在我的代码中,一个模板参数可以假设一个有限范围的值,我决定在建议下使用 std::variant 并在其中声明我可能会使用的所有对象需要:

std::variant<TemplateClass<1>, TemplateClass<2>, ..., TemplateClass<5>>

我从来没有用过这个工具。

要访问 TemplateClass 的方法,我必须使用 std::visit,但有时它可以工作,而其他情况则不能,比如 no member function XXX in std::variant &lt; .... &gt; 或“在函数模板专业化的实例化中......” [我什至没有了解这里有什么问题]

具体来说,我正在使用Eigen::Tensor 库,当我调用rank()dimension(n) 之类的方法时,它可以工作,而对于dimensions()setRandom() 之类的方法则不起作用。

下面是我的实施草稿

std::variant<Eigen::Tensor<double, 1>, Eigen::Tensor<double, 2>, /* ... */> makeTensor(
    int i, const std::initializer_list<int> dims) {
  switch (i) {
    case 1: {
      Eigen::Tensor<double, 1> T1;
      T1.resize(dims);
      return T1;
    }

    case 2: {
      Eigen::Tensor<double, 2> T2;
      T2.resize(dims);
      return T2;
    }

      /* ... */
  }
}

int main() {
  auto myTensor{makeTensor(2, {4, 5})};  // Tensor 2D 4x5

  // Working methods
  auto rnk = std::visit([](const auto &tensor) { return tensor.rank(); }, myTensor);

  auto dim1 = std::visit([](const auto &tensor) { return tensor.dimension(0); }, myTensor);

  // Not working methods

  auto dimsTens =
      std::visit([](const auto &tensor) { return tensor.dimensions(); }, myTensor);  // 5 times same error saying
  //'In instantiation of function template specialization 'std::visit<(lambda at
  /// home/virginie/Desktop/Project/main.cpp:62:33),
  // std::variant<Eigen::Tensor<double, 1, 0, long>, Eigen::Tensor<double, 2, 0, long>, Eigen::Tensor<double, 3, 0,
  // long>, Eigen::Tensor<double, 4, 0, long>, Eigen::Tensor<double, 5, 0, long>> &>''

  std::visit([&myTensor]() { myTensor.setRandom(); });  // 'No member setRandom() in std::variant<...>'
}

我是否以错误的方式使用std::visit

---- 编辑----

在@florestan 的建议下,我已经解决了与dimensions() 相关的问题,而setRandom 我得到了以下信息:

在 /..../ main.cpp 包含的文件中 从这里需要

【问题讨论】:

  • 假设所有变体成员都有rank()dimension(int) 成员返回相同的类型,这似乎是正确的。如果您收到编译错误,则意味着并非所有这些都具有类成员,或者它们返回不同的类型。
  • dimensions() 应该返回一个长度等于张量阶数 (1,2,3,...5) 的数组。我怎么解决这个问题? setRandom() 怎么样,肯定它是一个类成员,因为如果我写 'Eigen::Tensor A({4,5})' 然后我调用 'A.setRamdom()' 它可以工作
  • 这是因为,很可能每个setRandom 都会返回一个void。所有对象都有显式类型是C++的基础。 auto rank= 必须有一些特定的推导类型。但是,如果访问者return 的类型不同,则根据变体中的值,auto rank 的实际类型必须有所不同,在运行时。 C++ 不能以这种方式工作。很可能是因为auto rnk 本身必须是所有可能返回类型的variant,并且由访问者直接分配,而不是返回,每个访问者返回都是无效的。
  • 作为参考,heredimensions() 函数,heresetRandom()。正如建议的那样,返回的类型DSizes 有一个用于排名的模板参数。也许你可以把它包装成Array&lt;IndexType, Dynamic, 1&gt;(例如ArrayXi)。
  • 对于setRandom(),在您发布的代码中您试图调用变体对象中的方法,您应该像在其他情况下一样通过访问者来执行此操作,例如std::visit([](const auto &amp;tensor) { tensor.setRandom(); }, myTensor);。跨度>

标签: c++ templates c++17 union eigen


【解决方案1】:

您需要为所有可能的选择返回相同的类型。以dimension为例,需要将数组元素复制到向量中。

这样的事情应该会有所帮助:

 auto dimsTens=std::visit(
    [](const auto &tensor) {
       auto dims = tensor.dimensions();
       return std::vector<int>(dims.begin(), dims.end());
    }, myTensor);

第二个错误是因为您没有以正确的方式调用std::visit。它需要两个参数,第一个是要访问的函数,第二个是要访问的变量。以下应该可以工作。

std::visit([](auto& t){ t.setRandom();}, myTensor); 

实时代码: https://godbolt.org/z/vq4PYo

【讨论】:

  • 我正在实现一个方法,但 std::visit 仍然存在问题。我想我对这个实用程序还不太了解,而且错误是难以理解的,并且不能帮助我理解什么是错误的。该方法应返回对象张量的副本,因此我处于根据变体具有不同返回类型的情况。我该如何处理?这是我的实现的链接。 godbolt.org/z/e37Mqd
  • 你的代码有几个问题。创建示例时请小心,例如MyTensormyTensor 不一样!在深入挖掘之后,问题似乎是tensor.shuffle(...) 没有返回张量,而是一个不能隐式转换为张量的张量表达式。我对 Eigen 的了解不够深入,无法告诉你如何解决这个问题。另一件事是,您不能从 std::visit 返回不同的类型,因此您需要在 lambda 中捕获结果张量变量(通过引用)并使用 lambda 进行分配。
  • 但是,你的整个方法似乎有点单调,如果你真的需要整个变体的东西,也许你应该考虑一下。也许您也可以使用 5 阶张量并将不必要的维度设为奇异?或者也许你可以以某种方式排除运行时依赖关系?
  • 我会考虑的,谢谢。再次
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-20
  • 1970-01-01
  • 1970-01-01
  • 2021-03-25
  • 2023-03-27
  • 2018-02-24
相关资源
最近更新 更多