2016-04-26 114 views
1

我有一个std::tuple填充从一个类型参数的类模板实例化的对象。现在我想在编译时从元组中获取具有指定类型参数的元素。这里是代码:不同的constexpr行为vs2015u2 vs gcc

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:表达式的结果不是一个常数
注:故障是由非恒定的参数或引用非恒定的符号
看到使用造成的'I'

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

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

+0

是否删除静态断言有什么区别? –

+0

唯一的区别是当static_assert被删除时,下一行的编译器扼流圈出现相同的错误。 – user6256186

+0

好吧,基本上,'I'是一个constexpr,编译器认为'std :: get (tuple)'不是一个constexpr。至于“如何解决这个问题”,答案将会是,“编译器必须修复”。 –

回答

0

如果我得到它的权利你试图做类似

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); 
} 
0

既然不能只是扔掉“坏”的编译器,我实现了解决方法。也许它会帮助别人。

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); 
}