如何转发 C++基地和通过模板构造函数的成员初始值设定项?
我想制作一个模板,其参数 B 和 D 是模板实例化中固定数组的基类和元素:
template <class B,class D>
struct fix_array : public B {
D myArray[5];
/**/ fix_array( void ) { cout << "completely default constructed"; }
};
这工作正常。接下来,我想将初始化值传递给 B:
template <class... BI>
/**/ fix_array( BI &&... bi )
: B( std::forward<BI>(bi)... )
, myArray( )
{ cout << "This works too, even for multi-argument constructors of B."; }
这也有效。最后,我的问题:
如何通过模板构造函数将初始化值传递给 D myArray[5];
?
我设想调用代码看起来像这样:
fix_array<B,D> myVal( { B-initializers }, { { D[0]-initializers }, { D[1]-initializers } } );
但我迷失在可变参数模板地狱中。所有 D
初始化器都必须具有相同的形式吗? D
可以用 int
、const char *
或 D &&
初始化。)
(假设 是我的测试程序。 debug-util.hh
定义了一个类 Vobject
,它在构造、析构、复制等时打印出信息。
#include <stdio.h>
#include <memory.h>
#include <iostream>
#include <typeinfo>
#include <memory>
#include <vector>
#include "debug-util.hh" // Defines Vobject and some other info-printing utilities
template <class B, class T>
class fix_array : public B
{
public:
T pData[5];
/**/ fix_array( void ) : B( ), pData( ) { std::cerr << "completely default construction" << std::endl; }
template <class... BI>
/**/ fix_array( BI &&... bi )
: B( std::forward<BI>(bi)... )
, pData( )
{
std::cerr << "fix_array Base was initialized" << std:: endl;
}
} ;
/*
* A Vobject has 4 constructors: (void), (int), (int,char*), (int,int,int)
*
* It prints information about how it's constructed, when it's copied or moved,
* and when it's destroyed.
*/
int
main( int, char ** )
{
fix_array<Vobject,Vobject> a; // These 4 initialize the Base Vobject
fix_array<Vobject,Vobject> b( 1 ); // using the various constructors.
fix_array<Vobject,Vobject> c( 3, "foobar" );
fix_array<Vobject,Vobject> d( 100, 200, 300 );
// HOW CAN I PASS INITIALIZERS TO THE pDATA ARRAY MEMBERS???
// fix_array<Vobject,Vobject> x( { 123, "base init" }, { { 1 }, { 2, "xyz" }, { 2, 4, 5 } } );
std::cerr << "Hello, world" << std::endl;
return 0;
}
(是的,我知道这是一个愚蠢的程序,它使用 std::vector 等会更聪明,但我有一个更复杂的最终目标,学习如何使用可变参数模板将帮助我实现它。)
该程序的执行:
Vobject@A000()
Vobject@A001()
Vobject@A002()
Vobject@A003()
Vobject@A004()
Vobject@A005()
completely default construction
Vobject@A006(1)
Vobject@A007()
Vobject@A008()
Vobject@A009()
Vobject@A010()
Vobject@A011()
fix_array Base was initialized
Vobject@A012(3, "foobar")
Vobject@A013()
Vobject@A014()
Vobject@A015()
Vobject@A016()
Vobject@A017()
fix_array Base was initialized
Vobject@A018(100,200,300)
Vobject@A019()
Vobject@A020()
Vobject@A021()
Vobject@A022()
Vobject@A023()
fix_array Base was initialized
Hello, world
~Vobject@A023 (was constructed Vobject(void))
~Vobject@A022 (was constructed Vobject(void))
~Vobject@A021 (was constructed Vobject(void))
~Vobject@A020 (was constructed Vobject(void))
~Vobject@A019 (was constructed Vobject(void))
~Vobject@A018 (was constructed Vobject(int,int,int))
~Vobject@A017 (was constructed Vobject(void))
~Vobject@A016 (was constructed Vobject(void))
~Vobject@A015 (was constructed Vobject(void))
~Vobject@A014 (was constructed Vobject(void))
~Vobject@A013 (was constructed Vobject(void))
~Vobject@A012 (was constructed Vobject(int,const char*))
~Vobject@A011 (was constructed Vobject(void))
~Vobject@A010 (was constructed Vobject(void))
~Vobject@A009 (was constructed Vobject(void))
~Vobject@A008 (was constructed Vobject(void))
~Vobject@A007 (was constructed Vobject(void))
~Vobject@A006 (was constructed Vobject(int))
~Vobject@A005 (was constructed Vobject(void))
~Vobject@A004 (was constructed Vobject(void))
~Vobject@A003 (was constructed Vobject(void))
~Vobject@A002 (was constructed Vobject(void))
~Vobject@A001 (was constructed Vobject(void))
~Vobject@A000 (was constructed Vobject(void))
I'd like to make a template whose parameters B and D are the base class and elements of a fixed array in the template instantiation:
template <class B,class D>
struct fix_array : public B {
D myArray[5];
/**/ fix_array( void ) { cout << "completely default constructed"; }
};
This works fine. Next, I'd like to pass initializing values to B:
template <class... BI>
/**/ fix_array( BI &&... bi )
: B( std::forward<BI>(bi)... )
, myArray( )
{ cout << "This works too, even for multi-argument constructors of B."; }
That works too. And finally, my question:
How can I pass initializing values to D myArray[5];
through a template constructor?
I envision calling code looking something like:
fix_array<B,D> myVal( { B-initializers }, { { D[0]-initializers }, { D[1]-initializers } } );
but I'm lost in variadic-template hell. Do all the D
initializers have to have the same form? (Suppose D
can be initialized with int
, const char *
, or D &&
.)
Here is my test program. debug-util.hh
defines a class Vobject
that prints out information when it's constructed, destructed, copied, etc.
#include <stdio.h>
#include <memory.h>
#include <iostream>
#include <typeinfo>
#include <memory>
#include <vector>
#include "debug-util.hh" // Defines Vobject and some other info-printing utilities
template <class B, class T>
class fix_array : public B
{
public:
T pData[5];
/**/ fix_array( void ) : B( ), pData( ) { std::cerr << "completely default construction" << std::endl; }
template <class... BI>
/**/ fix_array( BI &&... bi )
: B( std::forward<BI>(bi)... )
, pData( )
{
std::cerr << "fix_array Base was initialized" << std:: endl;
}
} ;
/*
* A Vobject has 4 constructors: (void), (int), (int,char*), (int,int,int)
*
* It prints information about how it's constructed, when it's copied or moved,
* and when it's destroyed.
*/
int
main( int, char ** )
{
fix_array<Vobject,Vobject> a; // These 4 initialize the Base Vobject
fix_array<Vobject,Vobject> b( 1 ); // using the various constructors.
fix_array<Vobject,Vobject> c( 3, "foobar" );
fix_array<Vobject,Vobject> d( 100, 200, 300 );
// HOW CAN I PASS INITIALIZERS TO THE pDATA ARRAY MEMBERS???
// fix_array<Vobject,Vobject> x( { 123, "base init" }, { { 1 }, { 2, "xyz" }, { 2, 4, 5 } } );
std::cerr << "Hello, world" << std::endl;
return 0;
}
(Yes, I understand that this is a silly program and it would be smarter to use std::vector
and so on. But I have a more-complicated end goal, and learning how to use variadic templates would help me reach it.)
Execution of this program:
Vobject@A000()
Vobject@A001()
Vobject@A002()
Vobject@A003()
Vobject@A004()
Vobject@A005()
completely default construction
Vobject@A006(1)
Vobject@A007()
Vobject@A008()
Vobject@A009()
Vobject@A010()
Vobject@A011()
fix_array Base was initialized
Vobject@A012(3, "foobar")
Vobject@A013()
Vobject@A014()
Vobject@A015()
Vobject@A016()
Vobject@A017()
fix_array Base was initialized
Vobject@A018(100,200,300)
Vobject@A019()
Vobject@A020()
Vobject@A021()
Vobject@A022()
Vobject@A023()
fix_array Base was initialized
Hello, world
~Vobject@A023 (was constructed Vobject(void))
~Vobject@A022 (was constructed Vobject(void))
~Vobject@A021 (was constructed Vobject(void))
~Vobject@A020 (was constructed Vobject(void))
~Vobject@A019 (was constructed Vobject(void))
~Vobject@A018 (was constructed Vobject(int,int,int))
~Vobject@A017 (was constructed Vobject(void))
~Vobject@A016 (was constructed Vobject(void))
~Vobject@A015 (was constructed Vobject(void))
~Vobject@A014 (was constructed Vobject(void))
~Vobject@A013 (was constructed Vobject(void))
~Vobject@A012 (was constructed Vobject(int,const char*))
~Vobject@A011 (was constructed Vobject(void))
~Vobject@A010 (was constructed Vobject(void))
~Vobject@A009 (was constructed Vobject(void))
~Vobject@A008 (was constructed Vobject(void))
~Vobject@A007 (was constructed Vobject(void))
~Vobject@A006 (was constructed Vobject(int))
~Vobject@A005 (was constructed Vobject(void))
~Vobject@A004 (was constructed Vobject(void))
~Vobject@A003 (was constructed Vobject(void))
~Vobject@A002 (was constructed Vobject(void))
~Vobject@A001 (was constructed Vobject(void))
~Vobject@A000 (was constructed Vobject(void))
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我怀疑你在试图同时传递两包时被卡住了。您可能会被困在那里,因为这是直观的事情。不幸的是,直觉在这里让我们失败了,因为从裸函数参数中进行包推导的行为方式是直觉所预料不到的。尾随包的行为不同,很难知道第二个包何时开始以及第一个包何时结束,并且一路上出现的错误并不总是很清楚。
在这种情况下,我喜欢简单地执行标准库所做的事情。
std::pair
是聚合两个元素的类型。它允许通过将一组参数传递给每个元素来单独(就地)初始化每个元素(构造函数 8)。这与std::forward_as_tuple
协同作用,一个标准的功能模板,用于转发。您可以通过执行类似的操作向基数和数组提供参数。剩下的问题只是如何提取元组元素。
B
在 C++17 的std 中很简单: :make_from_tuple
。myArray
没有同样的好处,因为 c 样式数组是不规则类型。然而,我们可以使用委托构造函数和实用程序 std::index_sequence 来构造数组。我也将在B
中使用这种方法,因此总共只需要 C++14。两个
index_sequence
参数是标记类型占位符,其中按顺序包含每个元组元素的索引。它们只是在那里扩展并访问正确序数的每个元组元素。在std::forward_as_tuple
完成其工作后,std::get
已经通过正确的引用类型提取元素。<一href="https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAys ib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIAPACYAQuYukl9ZATwDKjdAGFUtAK4sGIM6SuADJ4DJgAcj4ARpjEIADMAQAOq AqETgwe3r7%2BpClpjgIhYZEsMXGJtpj2hQxCBEzEBFk%2BfgF2mA4Z9Y0ExRHRsQntDU0tOSO9/aXlwwCUtqhexMjsHObxocje WADUJvFuBF5J9AfYJhoAghtbO5j7h16OtIQAnueXN2abDNteewObgUBGIoWAN2utz%2B90ebicIOImFYkJu1wImBYpyYGLh2yYC guuxsu3xhN2ABVUWSiTRVAB9RrEJhvXYgXZJLxRV7IYlfEwadis1053LwyBAX120t2GKxBlxQJpADpVcSAEqYKgKUikgyE1XK3Y AEU12tRMt2dMZxGZbwgIPQIBAx1OmCBFjNCkN512USuxGAOt2judrrOh1NWu9qt96ADQbmUpl7OtTJZDoITpALFQADdMBB/YGFA sQ1nnSwmABrTD00JYBlKACOXkYqyBaQAXphUFRDRBPdGk/ELkLBcbdaGc/nC/GS2Xp1Xa/WGI36S22393Ydu73%2B6qIFHtSOx1 YBcak9dLYLz8b%2BSKwXmcexk9KKZbPzKksacUwTAArBYAGAfe8TCmiVyWnK2KKocKpqkO2pThWIB7vSBCGsSeDoKouoIUax7Bt O6GYWqxo4aoFoymmtoZtO4Y7m4SExsqvrFkGKHZg2mBNpgrbtkxFiUT6o5cWGJwRm4RGidguzzpx0rTjxfECduQIUbhslXlBKb EHA5bZsAmAEB6lHnEWCallhOmWrqP5/rehnOsZpmRuZo4QAp1lqhOb77OO15fs5KCxMQcJAvsZhmlLRdrEgSDwaO4ErsoTpGIea9 ug5hmBFhwhbsri0AcEE3he/LleBD5XIiXgOHy46VRBXyhaAQuxVqEEA6be/mxSyHriWhoLgr6VC3tO/DEMlxDoIyCgYZJhaLqhk3 TbNBILW6EA5f%2B0W6jlUQ5ft0WmHtUVmNlZ0kcNRhdUmTUVcaHALLQnAAbwfgcFopCoJwbjWNYIZLCsDwbDwpAEJoz0LNWIAAR o%2BicJIH1Qz9nC8AoIAI5DX3PaQcCwEgvGdM8JDkJQjRBsohjVEICCoIln3g2gWJ0DiGTU2EtB0wzn3fSzSR0EMwBcFwAQC0Lx AAPLPDzjOo8TyBWejgSqJ09T4J9vD8IIIhiOwUgyIIigqOouOkLoXD6IYxgA5Y%2Bh4FEmOQAsqBJLUmMcAAtF7joHMap1WJYUi 7F7SSYOghiOMgXthSQRJe1LZgY8Dqx6I6oSc7T9Py9wvCgpgazg4lzJJJwPAvW9KPm79HDYGryCk%2BFqgABwAGxe23ki7MAyC8 qLyq5RA/2WNYuq4IQJBRfEXBzPnUNzAsCDIlgcRdaQsPw4jHDI6QfO8LXGNYxDC%2BVxwyd76jh8n7ji%2BkAWxBpM4khAA%3D" rel="nofollow noreferrer">您可以在实例中看到它的工作原理。
I suspect you got stuck when trying to pass two packs at the same time. You likely got stuck there because it's the intuitive thing to do. Unfortunately, intuition fails us here because pack deduction from bare function arguments behaves in ways intuition doesn't expect. Trailing packs behave differently, there is difficulty in knowing when the second pack starts and the first ends, and the errors one gets along the way aren't always clear.
In situations like these I like to simply do what the standard library does.
std::pair
is a type that aggregates two elements. And it allows initializing each one individually (in place) by passing a tuple of parameters to each element (constructor 8). This synergizes withstd::forward_as_tuple
, a standard function template the does forwarding. You can supply arguments to both the base and the array be doing something similar.The remaining question is only how to extract the tuple elements.
B
is easy in C++17'sstd::make_from_tuple
.myArray
doesn't have the same benefit, since c-style arrays are irregular types. We can however construct the array with a delegating constructor and an utilitystd::index_sequence
. I'll be using this approach forB
as well, thus requiring only C++14 in total.The two
index_sequence
parameters are tag type placeholders that contain the index of every tuple element in order. They are simply there to expand upon and access each tuple element at the correct ordinal.std::get
already extract the elements by correct reference type afterstd::forward_as_tuple
has done its thing.You can see it working in a live example.