【问题标题】:Get type from variant at compile time在编译时从变体中获取类型
【发布时间】:2019-03-26 00:18:31
【问题描述】:

我需要在编译时对变体类型是否可以保存类型进行类型检查。

我正在将枚举和字符串转换为变体,但我希望库与用户提供的变体兼容(对于它们支持的类型)。所以我有一个模板参数CustomVariant 来表示受支持类型子集AlphaBetaGammaDeltaEpsilon 的变体。如果我无法创建有效的变体,我想返回 std::nullopt

template <typename CustomVariant>
std::optional<CustomVariant> AsCustomVariant(LargeEnum type, const std::string& name) {
  case LargeEnum::ALPHA:
  case LargeEnum::BETA:
    return ConvertAlphaBeta(name);

  case LargeEnum::GAMMA:
    return ConvertGamma(name);

  case LargeEnum::DELTA:
    return ConvertDelta(name);

  case LargeEnum::EPSILON:
    return ConvertEpsilon(name);

  default:
    return std::nullopt;
}

我们的想法是使用某种模板魔法,它可以执行以下操作:

if (std::type_can_convert<CustomVariant, Gamma>) {
  return ConvertGamma(name);
} else {
  return std::nullopt;
}

【问题讨论】:

  • 什么是 CustomVariant?它是标准变体吗? Gamma 是一种类型吗?如果 tha 变体 hokds 一个可以转换的类型 - 从 ConvertGamma 的 reyurn 类型,那足够好,还是只有完全匹配?如果是第一个,什么样的排序?你的 switch 语句在哪里,它似乎丢失了。 LargeEnum 是连续的吗?
  • 您正在寻找的模板魔法是if constexpr (std::is_convertible_v&lt;Gamma, CustomVariant&gt;)

标签: c++ c++11 templates variant


【解决方案1】:

使用 c++17(我知道它是用 c++11 标记的),这非常容易 - 你甚至不必真正做任何事情:

#include <variant>
#include <type_traits>
#include <string>

using namespace std;

int main() {

    // this works, as expected
    if constexpr(is_constructible_v<variant<int>, double>) {
        // this will run
    }

    // this is fine - it just won't happen, 
    if constexpr(is_constructible_v<variant<int>, string>) {
        // this won't run
    } else {
        // this will run
    }
    // but obviously the assignment of a string into that variant doesn't work...
    variant<int> vi="asdf"s;
}

https://godbolt.org/z/I-wJU1

【讨论】:

    【解决方案2】:

    首先我会这样做:

    template<class T>struct tag_t{using type=T;};
    template<class T>constexpr tag_t<T> tag{};
    
    template<class...Ts>using one_tag_of=std::variant<tag_t<Ts>...>;
    
    using which_type=one_tag_of<AlphaBeta, Gamma, Delta /* etc */>;
    
    which_type GetType(LargeEnum e){
      switch (e){
        case LargeEnum::Alpha:
        case LargeEnum::Beta: return tag<AlphaBeta>;
        // etc
      }
    }
    

    现在我们这样做:

    template <typename CustomVariant>
    std::optional<CustomVariant> AsCustomVariant(LargeEnum type, const std::string& name) {
      auto which = GetType(type);
      return std::visit( [&name](auto tag)->std::optional<CustomVariant>{
        using type=typename decltype(tag)::type;
        if constexpr (std::is_convertible<CustomVariant, type>{})
          return MakeFromString( tag, name );
        return std::nullopt;
      }, which );
    }
    

    这会留下MakeFromString

    像这样编写重载:

    inline Delta MakeFromString(tag_t<Delta>, std::string const& name){ return ConvertDelta(name); }
    

    注意,不是专业。只是重载。

    【讨论】:

      猜你喜欢
      • 2013-08-20
      • 2014-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多