Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/124.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 分离类模板中的参数包_C++_Class_Templates_Variadic_Parameter Pack - Fatal编程技术网

C++ 分离类模板中的参数包

C++ 分离类模板中的参数包,c++,class,templates,variadic,parameter-pack,C++,Class,Templates,Variadic,Parameter Pack,我正在尝试编写一个类模板,它使用参数包并为参数包中包含的每个类型实现一个成员函数 这就是我到目前为止所做的: template <typename...T> class Myclass { public: void doSomething((Some_Operator_to_divorce?) T) { /* * Do Something */ std::cout << "I did s

我正在尝试编写一个类模板,它使用参数包并为参数包中包含的每个类型实现一个成员函数

这就是我到目前为止所做的:

template <typename...T>
class Myclass {
public:
    void doSomething((Some_Operator_to_divorce?) T) {
        /*
         * Do Something
         */
        std::cout << "I did something" << std::endl;
    }
};
模板
类Myclass{
公众:
无效数据处理((某些运营商离婚?){
/*
*做点什么
*/
std::cout没有专门支持这一点的“操作员”,但是根据您的需求,您请求的操作可以通过几种不同的方式完成

为了实现重载函数集,从类模板的参数包中“提取”
T
类型的唯一方法是使用递归继承实现它,其中每个实例提取一个“T”类型并实现该函数,将其余的传递给下一个实现

比如:

//提取第一个“T”,将“Rest”传递给下一个类型
模板
类MyClassImpl:公共MyClassImpl
{
公众:
void doSomething(const T&){…}
使用MyClassImpl::doSomething;
};
模板
类MyClassImpl//结束案例,不再“Rest”
{
公众:
void doSomething(const T&){…}
};
模板
类MyClass:公共MyClassImpl
{
公众:
使用MyClassImpl::doSomething;
...
};
这将实例化
sizeof…(Types)
类模板,其中每个模板为每个
T
类型定义一个重载

这可以确保获得重载语义——这样,传递
int
可以调用
long
重载,或者如果存在两个相互竞争的转换,则会产生歧义

但是,如果这不是必需的,那么使用
enable\u if
和一个条件使用SFINAE启用该功能会更容易

为了进行精确的比较,您可以创建一个
特征中的一个,只有当
T
正是其中的一种类型时才能确保它的存在。在C++17中,这可以通过
std::disjunction
std::is\u same
来完成:

#包括
//检查T是否属于“类型…”的特征
模板
结构是:std::析取{};
或者,您可能希望它仅在可转换类型下工作——您可以执行以下操作:

模板
结构是可转换的:std::析取{};
两者之间的区别在于,如果您将字符串文字传递给
MyClass
,它将与第二个选项一起工作,因为它是可转换的,而不是第一个选项,因为它是精确的。从模板推导出的
T
类型也将不同,前者正好是
类型中的一种…
,后者正好是后者er是可转换的(同样,
T
可以是
const char*
,但是
类型…
只能包含
std::string

要将这一点结合到您的
MyClass
模板中,您只需使用
enable\u if
使用SFINAE启用条件:

模板
类MyClass
{
公众:
//仅当“T”正好是“类型…”之一时实例化
模板
void doSomething(const T&){…}
//或
//仅当T可转换为“类型…”之一时实例化
模板
void doSomething(const T&){…}
};
哪种解决方案适合您完全取决于您的需求(重载语义、精确调用约定或转换调用约定)


编辑:如果你真的想变得复杂,你也可以合并这两种方法……创建一个类型特征来确定从重载中调用什么类型,并使用它来构造特定底层类型的函数模板

这与需要实现的
variant
类似,因为它有一个
U
构造函数,将所有类型都视为重载集:

//创建所有函数的重载集,并为
//每种返回类型
模板
结构重载\u集\u impl;
模板
结构重载\u集\u impl
:过载\u设置\u执行
{
使用重载_set_impl::operator();
std::积分常数运算符()(T0);
};
模板
结构重载集:重载集\u impl{};
//获取通过使用T调用所有重载将返回的索引
模板
重载的结构索引:decltype(std::declval()(std::declval()){};
//从上面的测试中获取元素
模板
结构可构造_重载
:std::tuple_元素{};
模板
使用可构造的\u重载\u t
=typename可构造_重载::type;
然后将其与使用函数模板的第二种方法结合使用:

模板
类MyClass{
公众:
//仍然接受任何可转换的类型
模板
无效剂量测量(常数T&v)
{
//转换为特定的重载类型,并调用它
使用类型=可构造的\u重载\u t;
doSomethingImpl(v);
}
私人:
模板
void doSomethingImpl(const T&){…}
最后一种方法分两个阶段进行;它使用第一个SFINAE条件确保可以转换,然后确定将其视为的适当类型,并将其委托给实际(私有)实现

这要复杂得多,但可以实现类似重载的语义,而不需要在创建它的类型中实际递归实现

Myclass<std::string, int, double> M;

M.doSomething("I am a String");
M.doSomething(1234);
M.doSomething(0.1234);