可以/应该/将椭圆适用于元组吗?

时间:2013-12-21 19:33:46

标签: c++11 c++14

在C ++ 11中,可变参数模板允许使用任意数量的参数调用函数,并且省略号运算符...允许该可变参数函数对每个参数执行某些操作,即使这些参数不是每个论点都是一样的:

template<typename... Types>
void dummy(Types... ts){} //dummy to allow function calls via parameter expansion

template<typename... Numerics>
void increment5(Numerics&... ns){
    dummy(ns+=5 ...); //parameter expansion (need space after 5 because 5. is decimal)
    //it means "dummy(ns_first+=5, ns_second+=5, ns_third+=5, etc.)"
}

int main(){
    int i = 0;
    float f = 1.1;
    std::valarray<int> vi = {1,2,3};

    increment5(i,f,vi);

    cout
        <<i<<endl
        <<f<<endl
        <<vi[0]<<' '<<vi[1]<<' '<<vi[2]<<endl;

}

如果我们定义一个异构值数组(不同数字类型的列表),我们希望能够做同样的事情(能够为每个元素添加一个数字)。但我们必须将元素存储为元组。

//a class of numbers, possibly different
template<typename... Numerics>
class HeterogeneousValueArray{
private:
    tuple<Numerics...> inner;
public:
    //initialize the internal vector
    HeterogeneousValueArray(Numerics... ns): inner(ns...) {}
};

//Given this function, we don't have to explicitly give the types
//when constructing a HVA
template<typename... Numerics>
HeterogeneousValueArray<Numerics...> HeterogeneousValueArray(Numerics... ns){
    return HeterogeneousValueArray<Numerics...>(ns);
}

要调用上面的increment5运算符,我们需要进行元组扩展。 As I understand it, this solution would require defining helper functions for each function I want to write.我们也可以递归地定义increment5,但是,每个函数需要两个函数体。

我相信编程语言应该努力设计允许我们编写我们想要编写的代码。所以这就是我想写的。

template<typename... Numerics>
void increment5(HeterogeneousValueArray<Numerics...>& hva){
    increment5(hva.inner... ); //expand a tuple
}

或者这个。

template<typename... Numerics>
void increment5(HeterogeneousValueArray<Numerics...>& hva){
    dummy((hva.inner+5)... ); //expand a tuple
}

换句话说,我想将一个元组视为参数包。

当然,“编写您想要编写的代码”是理想主义的,实现任何功能都可能存在问题。什么样的问题会使这种功能无法正常工作(歧义?),或者它如何踩到现有代码或功能的脚趾?或者......它是否已经存在于C ++ 14中?

1 个答案:

答案 0 :(得分:5)

创建解决方案的基本工具存在于C ++ 14中,但您需要通过额外的辅助函数获得额外的间接级别,类似于您链接到的答案:

template<typename Tuple, std::size_t... I>
void
_increment5(Tuple& t, std::index_sequence<I...>)
{
  dummy( std::get<I>(t) += 5 ... );
}

template<typename... Numerics>
void
increment5(HeterogeneousValueArray<Numerics...>& hva)
{
  _increment5(hva.inner, std::index_sequence_for<Numerics...>());
}

std::index_sequence_for<T1, T2, T3>是类型std::index_sequence<0, 1, 2>的别名,因此当与包扩展一起使用时,它会为参数包的元素创建一系列索引。然后可以通过辅助函数将该序列索引推断为另一个参数包,因此包扩展std::get<I>(t)...将扩展I以提取元组的每个元素。

index_sequence添加到C ++ 14的提案还添加了一个示例,显示了将函数对象应用于元组的通用apply函数,这将允许@DyP在上面的注释中建议:

template<typename... Numerics>
void
increment5(HeterogeneousValueArray<Numerics...>& hva)
{
  apply(hva.inner, [](auto& n) { n += 5; });
}

apply函数不在C ++ 14中(但对于C ++已经是proposed 17)所以你需要自己编写它,例如:通过从C ++ 14草案中复制它。

最接近直接允许您想要的内容的提案是N3728,但尚未被接受,因此不会出现在C ++ 14中。