如何使用访问者从变量返回特定类型?

时间:2018-11-05 17:54:35

标签: c++ templates c++17 variadic-templates std-variant

我有下面的代码,为什么visitor1visitor2给出错误? 这是否意味着访问者无法在变体中返回一种类型?

#include <iostream>
#include <variant>


struct Visitor1
{
    template <class T>
    T operator()(const T & t) const
    {
        return (t);
    }
};

struct Visitor2
{
    int operator()(const int  & t) const
    {
        return std::get<int>(t);
    }

    char operator()(const char & t) const
    {
        return std::get<char>(t);
    }
};

struct Visitor3
{
    void operator()(const int & t) const
    {
        std::cout<<t;
    }
    void operator()(const char & t) const
    {
        std::cout<<t;
    }
};

int main()
{
    std::variant<int, char> v{char(100)};

    std::visit(Visitor3{}, v);

    auto t = std::visit(Visitor2{}, v);  //fails
    //auto t = std::visit(Visitor1{}, v); //fails
    std::cout << t;
}

我知道我可以使用std::get(),但是问题是我只能将autostd::get()一起使用,如果我执行以下操作,则无法访问x在if / else范围之外:

bool b;
Variant v;
if (b)
{
   auto x = std::get<int>(v);
}
else
{
   auto x = std::get<char>(v);
}
// I want to do something with x here out of if/else

3 个答案:

答案 0 :(得分:2)

  

我有下面的代码,为什么visitor1和visitor2给出错误?

因为C ++是一种强类型语言。

写作时

auto t = std::visit(Visitor2{}, v);  //fails

编译器必须决定编译类型为t的编译时间,因此必须决定哪种类型返回std::visit(Visitor2{}, v)

如果Visitor2返回char,则当v包含char时,或者返回int,当v包含{{1 }},编译器无法选择(编译时!)从int返回的类型[也存在std::visit()中的Visitor2的问题(仅t) }是operator()int,因此无法对其应用char

std::get()相同的问题:模板Visitor1返回模板类型,因此operator()的{​​{1}}或int

char之所以有效,是因为两个std::variant<int, char>都返回Visitor3,因此编译器可以解析(编译时)operator()返回(在某种意义上)void

也许在this page中有更好的解释:

  

[{std::visit(Visitor3{}, v)]有效地返回

void
     

,其中std::visit()std::invoke(std::forward<Visitor>(vis), std::get<is>(std::forward<Variants>(vars))...) 。从返回的表达式推论出返回类型,就像is...一样。

     

对于所有变体的替代类型的所有组合,如果上述调用不是同一类型和值类别的有效表达式,则调用格式错误

答案 1 :(得分:2)

一种语言可能具有C ++的许多功能,可以满足您的需求。

为了做您想做的事,当您调用std::visit时,必须编写该函数其余部分的N种不同实现。

在这N种不同的实现方式(您的情况是2种)的每一种中,变量的类型都会不同。

C ++不能那样工作。

唯一与访问调用“相乘”的部分是访问者。

int main()
{
  std::variant<int, char> v{char(100)};

  std::visit([&](auto && t){
    std::cout << t;
  }, v);
}

我将函数的其余部分放置在访问者中。对于访问者中可以存储的每种类型,该代码都会实例化一次。

返回的所有内容都可以返回到调用范围的“单个实例”正文。

基本上,[&](auto&& t) lambda可以执行您想要的操作。


现在,我们可以做一些技巧来稍微更改语法。

我的最爱是:

v->*visit*[&](auto&& val) {
  std::cout << val;
  return [val](auto&& x) { x << val; };
}->*visit*[&](auto&& outputter) {
  outputer(std::cout);
};

其中->*visit*使用相对荒谬的元编程来允许

  1. 导致访问的指定运营商

  2. 将访问的返回值融合到一个变体中。

但没有理智的人会编写该代码。

答案 2 :(得分:1)

你可以

import { Pipe, PipeTransform } from "@angular/core";
import { Opportunity } from "../models/Opportunity";

@Pipe({
    name: "orderBy",
    pure: false
})

export class OrderByPipe implements PipeTransform {
    /**
     * Method to sort data and return sorted data
     * 
     * @param records 
     * @param args 
     */
    transform(records: Array<any>, args?: any): any {
        return records.sort(function (a, b) {
            if (a[args.property] < b[args.property]) {
                return -1 * args.order;
            }
            else if (a[args.property] > b[args.property]) {
                return 1 * args.order;
            }
            else {
                return 0;
            }
        });
    }
}