【问题标题】:constexpr function to add an integer to an arrayconstexpr 函数将整数添加到数组中
【发布时间】:2013-05-23 12:40:34
【问题描述】:

我正在尝试实现一个 constexpr 函数“add42”,它可以让我这样做:

constexpr array<int,5> arr = {1,2,3,4,5};
constexpr array<int,5> arr2 = add42(0,arr); //I want arr2=={43,2,3,4,5}

也就是说,将给定索引处的整数静态添加到数组中(使用 constexpr)。 由于我的数组“arr”是不可变的,我实际上必须从“arr”和我的索引创建一个新数组。这就是我编写这个函数的原因:

template<int DIM, typename... ARGS> auto constexpr
add42(int index, array<int,DIM> intergerArray, ARGS... unpackedIntegers) -> array<int,DIM> {
    return
        ( sizeof...(ARGS)==DIM ) ?
            array<int,DIM>( {{unpackedIntegers...}} ) :
        ( (sizeof...(ARGS)-1)==index ) ?
            add42(index, intergerArray, unpackedIntegers..., intergerArray[sizeof...(ARGS)-1]+42 ) :
            add42(index, intergerArray, unpackedIntegers..., intergerArray[sizeof...(ARGS)-1] ) ;
}

也就是说,我的数组的所有整数都从数组中递归解包,如果在正确的索引处添加 42,并附加在 ARGS 列表的末尾。当这个 arg 列表包含数组中的所有整数时,我们就完成了,我们可以重新打包到一个新数组中。

但是我得到了这个错误(gcc 4.7.2)

error: no matching function for call to 'add42(int, const std::array<int, 5u>&)'|
note: candidate is:|
template<int DIM, class ... ARGS> constexpr std::array<int, DIM> add42(int, std::array<int, DIM>, ARGS ...)|
note:   template argument deduction/substitution failed:|
note:   mismatched types 'int' and '#'integer_cst' not supported by dump_type#<type error>'|

你能解释一下是什么问题以及如何解决它吗?

这个问题似乎类似于C++11: Compile Time Calculation of Array 但不是(至少,我无法弄清楚如何直接使用它):在这里,我想从一个已经存在的创建一个新数组> 一个,不是来自已知的整数序列。

编辑

现在我得到了无限的实例化,即使没有调用递归。这是一个简化的例子:

template<size_t DIM, typename... ARGS> auto constexpr
add42(int index, array<int,DIM> integerArray, ARGS... unpackedIntegers) -> array<int,DIM> {
    return
        ( true ) ?
            array<int,DIM>( {{unpackedIntegers...}} ) :
            add42(index, integerArray, unpackedIntegers..., integerArray[(sizeof...(ARGS)-1)] ) ;
}

为什么我的编译器会尝试编译最后一个函数调用?

编辑 2

显然,为了不混淆编译器,我必须提供 2 个函数:

template<size_t DIM, class... ARGS> constexpr auto
add42(int index, array<int,DIM> integerArray, ARGS... unpackedIntegers) -> typename enable_if<sizeof...(ARGS)==DIM ,array<int,DIM>>::type
{
    return array<int,DIM>( {{unpackedIntegers...}} );
}
template<size_t DIM, class... ARGS> constexpr auto
add42(int index, array<int,DIM> integerArray, ARGS... unpackedIntegers) -> typename enable_if<sizeof...(ARGS)!=DIM ,array<int,DIM>>::type
{
    return
        ( sizeof...(ARGS) == index ) ?
            add42(index, integerArray, unpackedIntegers..., integerArray[sizeof...(ARGS)]+42) :
            add42(index, integerArray, unpackedIntegers..., integerArray[sizeof...(ARGS)]) ;
}

但还是不行:

recursively required from [[name of the second function]]

显然,可变参数函数不能“递归”调用其重载之一。 我对吗 ?有什么解决办法?

【问题讨论】:

  • sizeof 返回一个size_t,它是一个无符号整数类型。您可能不想从可能是 0 的无符号整数中减去 1(第一次递归,sizeof...(ARGS)0)。
  • @DyP 我的错。我编辑删除了这些-1。但还是不行:(
  • 使用 constexpr operator[] 和 clang 3.2 它可以工作,尽管你可能最好在第一个 add42 重载中只使用一对大括号——或者只使用 return {unpackedIntegers...};。 AFAIK 第二对调用复制ctor。 (一对用于 ctor,一对用于成员数组初始化。)

标签: c++ c++11 constexpr


【解决方案1】:

你应该使用

template<size_t DIM, typename... ARGS> auto constexpr
add42(int index, array<int,DIM> intergerArray, ARGS... unpackedIntegers)

因为数组的第二个参数的类型是size_t,而不是int

【讨论】:

  • 好的,谢谢。这解决了这个问题:) 但是,它仍然无法正常工作(见编辑)
【解决方案2】:

很遗憾,std::array::operator[] 不是 constexpr

编译器选项:简单的-std=c++11

gcc -std=c++0x 并将 using 指令替换为 typedef

#include <cstddef>
//#include <array>
#include <type_traits>
#include <iostream>

template < typename T, std::size_t dim >
struct c_array
{
    T arr[dim];

    constexpr T operator[](std::size_t index)
    {  return arr[index];  }

    T const* begin() const
    {  return arr;  }
    T const* end() const
    {  return arr+dim;  }
};


// I like the overloaded version better (instead of enable_if) :)

template < typename T, std::size_t dim, typename... TT >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, std::size_t index, std::true_type, TT... pp)
{
    return {{pp...}};
}

template < typename T, std::size_t dim, typename... TT >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, std::size_t index, std::false_type, TT... pp)
{
    using test = std::integral_constant<bool, (sizeof...(pp)+1 == dim)>;

    return   index == sizeof...(pp)
           ? add_to(s, in, index, test{}, pp..., in[sizeof...(pp)]+s)
           : add_to(s, in, index, test{}, pp..., in[sizeof...(pp)]  );
}


// unfortunately, I don't know how to avoid this additional overload :(

template < typename T, std::size_t dim>
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, std::size_t index)
{
    return add_to(s, in, index, std::false_type{});
}


constexpr c_array<int,5> arr = {1,2,3,4,5};
constexpr c_array<int,5> arr2 = add_to(42, arr, 0); //I want arr2=={43,2,3,4,5}

int main()
{
    for(auto const& e : arr2)
    {
        std::cout << e << ", ";
    }
}

替代版本,使用语法略显尴尬:

// helper; construct a sequence of non-type template arguments
template < std::size_t... tt_i >
struct seq
{};

template < std::size_t t_n, std::size_t... tt_i >
struct gen_seq
    : gen_seq < t_n-1, t_n-1, tt_i...>
{};

    template < std::size_t... tt_i >
    struct gen_seq < 0, tt_i... >
        : seq < tt_i... >
    {};

template < std::size_t index, typename T, std::size_t dim,
           std::size_t... tt_bef, std::size_t... tt_aft >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, seq<tt_bef...>, seq<tt_aft...>)
{
    return {{ in[tt_bef]..., in[index]+s, in[tt_aft]... }};
}

template < std::size_t index, typename T, std::size_t dim >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in)
{
    return add_to<index>(s, in, gen_seq<index>{}, gen_seq<dim-index-1>{});
}


constexpr c_array<int,5> arr = {1,2,3,4,5};
constexpr c_array<int,5> arr2 = add_to<0>(42, arr);

【讨论】:

  • 好的,谢谢。我不介意使用 std::array 以外的其他容器,我认为这是对 ISO 标准的一个小疏忽。但在 GCC 4.7.2 上,它不起作用。我有同样的错误。你能用你使用的特定版本的clang编辑你的答案吗?用什么选项(例如-std=c++14)我会把它标记为已解决。
  • @BérengerBerthoul Hm gcc 4.7 似乎需要第二对大括号。编译器选项只是-std=c++11。使用编辑后的代码重试。
  • 它不适用于 mingw32-g++.exe -Wall -g -O3 -Wall -std=gnu++11 -Wextra -Wall(也没有 -O3)。但正如你所看到的,我在 Windows 上运行它,所以这可能是不同的
  • @BérengerBerthoul 我在 g++ 4.7.2 mingw 上得到的只是使用这些选项的警告......但没有错误
  • 只有一个问题:这段代码有意义吗?我的意思是,稍微修改一下,我们就可以在编译时添加两个数组,这应该很常见。然而,没有迹象表明这种方法在任何地方都可以使用......
猜你喜欢
  • 2019-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-15
  • 2013-07-17
  • 1970-01-01
相关资源
最近更新 更多