【问题标题】:Different constexpr behavior vs2015u2 vs gcc不同的 constexpr 行为 vs2015u2 vs gcc
【发布时间】:2016-08-20 06:07:26
【问题描述】:

我有一个std::tuple,其中填充了从具有一个类型参数的类模板实例化的对象。现在我想在编译时从我的元组中获取一个具有指定类型参数的元素。代码如下:

template<typename Params, typename Descriptor>
struct IsParamsEqual;

template<typename Params1, typename Params2, ApiCommand::Value value>
struct IsParamsEqual<Params1, Descriptor<value, Params2>>
{
    static constexpr bool v = std::is_same<Params1, Params2>::value;
};

template<typename Params, size_t I, typename... Args>
constexpr size_t getIndexByParamsHelper(const IndexSequence<I>&, const std::tuple<Args...> &)
{
    return I;
}

template<typename Params, size_t I, size_t... Indexes, typename... Args>
constexpr size_t getIndexByParamsHelper(const IndexSequence<I, Indexes...> &, 
                                        const std::tuple<Args...> &tuple)
{
    return IsParamsEqual<Params, typename std::tuple_element<I, std::tuple<Args...>>::type>::v ?
           I : getIndexByParamsHelper<Params>(IndexSequence<Indexes...>(), tuple);
}

template<typename Params, size_t... Indexes, typename... Args>
constexpr size_t getIndexByParams(const IndexSequence<Indexes...> &seq, 
                                  const std::tuple<Args...> &tuple)
{
    return getIndexByParamsHelper<Params>(seq, tuple);
}

template<typename Params, typename... Args>
constexpr auto getByParamsImpl(const std::tuple<Args...> &tuple)
{
    constexpr size_t I = getIndexByParams<Params>(
        typename MakeIndexSequence<sizeof...(Args)>::type(), tuple);
    static_assert(std::is_same<typename std::remove_reference<decltype(
         std::get<I>(tuple))>::type::paramType, Params>::value,
         "Param not found");
    return std::get<I>(tuple);
}

这在 gcc 4.8.4 上编译得很好,但在 vs2015u2 上编译得不好。错误在getByParamsImpl() 中,它说:

错误 C2131:表达式未计算为常量
注意:失败是由非常量参数或对非常量符号的引用引起的
查看“我”的用法

很明显,编译器认为getIndexByParams()返回值不是constexpr。

为什么,以及 - 更重要的是 - 如何解决这个问题?

【问题讨论】:

  • 删除静态断言有什么不同吗?
  • 唯一的区别是当 static_assert 被删除时,编译器在下一行出现同样的错误。
  • 好的,所以,基本上,I 是一个 constexpr,编译器认为 std::get&lt;I&gt;(tuple) 不是一个 constexpr。至于“如何解决这个问题”,答案几乎是“必须修复编译器”。

标签: c++ c++11 gcc visual-studio-2015 variadic-templates


【解决方案1】:

如果我猜对了,你会尝试做类似的事情

constexpr std::tuple<int, float, int> t1{ 8, 2.0f, 1 };
constexpr std::tuple<int, float, double> t2{ 1, 2.0f, 3.0 };
constexpr std::tuple<unsigned, float, double> t3{ 1u, 2.0f, 3.0 };

constexpr auto f1 = find_by_type<int>(t1);
constexpr auto f2 = find_by_type<double>(t2);
constexpr auto f3 = find_by_type<unsigned>(t3);

在哪里

  • f1 应该等同于 constexpr int f1 = 8;
  • f2 应该等同于 constexpr double f2 = 3.0;
  • f3 应该等同于 constexpr unsigned f3 = 1u;

你可以通过参数包递归得到这样的东西:

// helper
template<class ... Ts> struct Finder;
// empty to have compiler error if type is not found
template<class T> struct Finder<T> {  };

// if first type in pack is type to find, we stop
template<class ToFind, class ... Ts>
struct Finder<ToFind, ToFind, Ts...>
{
  constexpr static std::size_t N = 0u;
};

// if first type in pack is not type to find
// we increase by one and go down the pack
template<class ToFind, class First, class ... Amongst>
struct Finder<ToFind, First, Amongst...>
{
  constexpr static std::size_t N = 1u + Finder<ToFind, Amongst...>::N;
};

template<class P, class ... Args>
constexpr auto find_by_type(const std::tuple<Args...> &t)
{
  return std::get<Finder<P, Args...>::N>(t);
}

【讨论】:

    【解决方案2】:

    因为我不能扔掉“坏”的编译器,所以我已经实施了解决方法。也许它会帮助某人。

    template<typename Params, typename Descriptor>
    struct IsParamsEqual;
    
    template<typename Params1, typename Params2, ApiCommand::Value value>
    struct IsParamsEqual<Params1, Descriptor<value, Params2>>
    {
        static constexpr bool v = std::is_same<Params1, Params2>::value;
    };
    
    template<typename Params, typename IS, typename... Args>
    struct GetIndexByParam;
    
    template<typename Param, typename... Args>
    struct GetIndexByParam<Param, IndexSequence<>, Args...>
    {
        typedef typename std::integral_constant<size_t, 0> type;
    };
    
    template <typename Param, size_t I, size_t... IndexesTail,  typename Head, 
        typename ...Tail>
    struct GetIndexByParam<Param, IndexSequence<I, IndexesTail...>, Head, Tail...>
    {
        typedef typename std::conditional<IsParamsEqual<Param, Head>::v,
            std::integral_constant<size_t, I>,
            typename GetIndexByParam<Param, IndexSequence<IndexesTail...>, Tail...>::type >::type type;
    };
    
    template<typename Params, typename... Args>
    constexpr auto getByParamsImpl(const std::tuple<Args...> &tuple)
    {
        typedef typename GetIndexByParam<Params,        
            typename MakeIndexSequence<sizeof...(Args)>::type, Args...>::type IndexValueType;
        static_assert(std::is_same<
            typename std::remove_reference<decltype(std::get<IndexValueType::value>(tuple))>::type::paramType, \
                Params>::value, "Parameter not found");
        return std::get<IndexValueType::value>(tuple);
    }
    

    【讨论】:

      猜你喜欢
      • 2016-07-24
      • 1970-01-01
      • 2017-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-09
      • 2015-12-23
      • 2016-07-29
      相关资源
      最近更新 更多