c 11 参数包可以在模板外使用吗?

新手上路,请多包涵

我想知道我是否可以拥有由单个明确指定的类型组成的参数包。例如,像这样:

 #include <iostream>

using namespace std;

void show() { }

template<typename First, typename... Rest>
void show(First f, Rest... rest)
{
    cout << f << endl;
    show(rest...);
}

void foo(int f, int... args) // error
{
    show(f, args...);
}

int main()
{
    foo(1, 2, 3);
}

我遇到的问题是 foo() 的定义。使用 OS X clang++ 版本 5 (llvm 3.3svn) 我收到错误 error: type 'int' of function parameter pack does not contain any unexpanded parameter packs

当然,我可以通过将 foo() 更改为函数模板来编译它:

 template<typename... Args>
void foo(int f, Args... args)
{
    show(f, args...);
}

但是现在 foo() 将接受 int 作为第一个参数,其余的任何输出都可以流式传输。例如:

 struct x { };
ostream& operator<<(ostream& o, x)
{
    o << "x";
    return o;
}

int main()
{
    foo(1, 2, x(), 3); // compiles :(
}

现在,我在 这里看到了公认的解决方案, 它建议使用类型特征和 std::enable_if ,但这很麻烦。他们还建议使用 std::array 但我认为一个简单的 std::initializer_list 工作得很好,看起来更干净,就像这样:

 void foo_impl(initializer_list<int> ints)
{
    for(int i: ints)
        cout << i << endl;
}

template<typename... Args>
void foo(int f, Args... args)
{
    foo_impl({f, args...});
}

struct x { };
ostream& operator<<(ostream& o, x)
{
    o << "x";
    return o;
}

int main()
{
    foo(1, 2, 3);
    foo(1, 2, x(), 3); // no longer compiles
                       // we also get an error saying no known conversion from 'x' to 'int' :)
}

所以这很整洁。但问题仍然存在,这有必要吗?真的没有办法定义一个接受特定类型参数包的非模板函数吗?像这样:

 void foo(int... args) { }

原文由 stack_lexi 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 873
1 个回答
void foo(int... args) {}

不,你不能那样写。

但是您可以使用这种方法获得相同的效果:

 template<typename ...Ints>
void foo(Ints... ints)
{
   int args[] { ints... }; //unpack ints here
   //use args
}

使用这种方法,您可以根据需要传递所有 int 。如果传递给 foo 的任何参数不是 int 或可转换为 int ,上面的代码将导致编译错误,因为它会是这种情况 int ...args 如果允许就接近。

您也可以使用 static_assert 来确保所有 Ints 确实是 int 如果您想要这种行为:

 template<typename ...Ints>
void foo(Ints... ints)
{
   static_assert(is_all_same<int, Ints...>::value, "Arguments must be int.");

   int args[] { ints... }; //unpack ints here
   //use args
}

现在你要实现 is_all_same 元函数,这并不难实现。

好的,这是基本的想法。您可以使用可变参数模板并借助一些实用元函数和辅助函数编写更复杂的代码。

对于可以使用可变参数进行的大量工作,您甚至不需要存储在 args[] 数组中,例如,如果您想将参数打印到 std::ostream ,那么您可以只需这样做:

 struct sink { template<typename ...T> sink(T && ... ) {} };

template<typename ...Ints>
void foo(Ints... ints)
{
    //some code

     sink { (std::cout << ints)... };
}

在这里,您创建一个类型为 sink 的临时对象,以便使用列表初始化语法解压缩模板参数。

最后你可以使用 std::initializer_list<int> 本身:

 void foo(initializer_list<int> const & ints)
{

}

或者 std::vector<int> 以防您需要在 foo() 内部进行类似矢量的行为。如果您使用其中任何一个,则在调用函数时必须使用 {}

 f({1,2,3});

这可能并不理想,但我认为随着 C++11 的出现,您会经常看到这样的代码!

原文由 Nawaz 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题