[英]Type conversion in class template instantiation
I have a template class item
which stores objects of various types T
. 我有一个模板类
item
,用于存储各种类型T
对象。 It also attaches attributes to those objects in instantiation/initialization. 它还在实例化/初始化中将属性附加到那些对象。
One special thing I want to achieve is that whenever item
sees a const char *
, it deems and stores it as a std::string
. 我要实现的一件事是,每当
item
看到const char *
,它将视为并将其存储为std::string
。 This could be done, as follows. 可以这样做,如下所示。
But in type checking, I found an item
instantiated from a const char *
is still different in type from an item
instantiated from a std::string
. 但在类型检查,我发现了一个
item
从一个实例const char *
是在类型从另一个不同的item
从一个实例化std::string
。 Please see the last line with comment false
, which I want to make true
. 请参见最后一行带有
false
注释,我想使之true
。
#include <iostream>
#include <string>
#include <type_traits>
using namespace std;
template<typename T>
using bar = typename std::conditional<std::is_same<T, const char *>::value,
string, T>::type;
template<typename T>
class item
{
bar<T> thing;
// other attributes ...
public:
item(T t) : thing(t) {}
// other constructors ...
bar<T> what() const
{
return thing;
}
};
int main()
{
auto a = item("const char *"); // class template argument deduction (C++17)
auto b = item(string("string")); // class template argument deduction (C++17)
cout << std::boolalpha;
cout << (typeid(a.what()) == typeid(b.what())) << endl; // true
cout << (typeid(a) == typeid(b)) << endl; // false
}
My question is: is it possible to make any change to the template class item
so that an item
instantiated from a const char *
becomes the same in type with an item
instantiated from a std::string
? 我的问题是:是否有可能进行任何更改模板类
item
,这样的item
从一个实例const char *
变成类型相同与item
从一个实例化std::string
?
In other words, can I make any change to the design of the template class item
so that typeid(a) == typeid(b)
evaluates to true ? 换句话说,我可以对模板类
item
的设计进行任何更改,以便typeid(a) == typeid(b)
评估为true吗?
Thank you ! 谢谢 !
Note: This follows up a previous question on template function. 注意:这是关于模板功能的先前问题的后续内容。 But I think there's something intrinsically different that it deserves a stand-alone question.
但是我认为应该在本质上有所不同,这值得一个独立的问题。
Edit: My goal is to change the design of the template class item
(eg item
signatures), not the code in main
, which is assumed to be supplied by users. 编辑:我的目标是更改模板类
item
的设计(例如, item
签名),而不是main
的代码,该代码假定是由用户提供的。 I want to make life easier for the users of item
, by not asking them to explicitly supply type T
in instantiation. 我想通过不要求他们在实例化中显式提供
T
类型,使item
的用户的生活更轻松。 This is meant to be done by C++17 template class argument deduction or some equivalent workarounds. 这可以通过C ++ 17模板类参数推导或某些等效的解决方法来完成。
Update: Thank you all! 更新:谢谢大家! Special thanks to @xskxzr, whose one-liner exactly solves my question.
特别鸣谢@xskxzr,他的一句话就能解决我的问题。 With user-defined deduction guides for class template argument deduction, I don't even need the
bar<T>
technique in my previous code. 有了用于类模板参数推论的用户定义推论指南 ,我甚至在以前的代码中甚至不需要
bar<T>
技术。 I put updated code below for your comparison. 我在下面放了更新的代码供您比较。
#include <iostream>
#include <string>
using namespace std;
template<typename T>
class item
{
// UPDATE: no bar<T> needed any more
T thing;
// other attributes ...
public:
item(T t) : thing(t) {}
// other constructors ...
// UPDATE: no bar<T> needed any more
T what() const
{
return thing;
}
};
item(const char *) -> item<std::string>; // UPDATE: user-defined deduction guide !
int main()
{
auto a = item("const char *"); // class template argument deduction (C++17)
auto b = item(string("string")); // class template argument deduction (C++17)
cout << std::boolalpha;
cout << (typeid(a.what()) == typeid(b.what())) << endl; // true
cout << (typeid(a) == typeid(b)) << endl; // UPDATE: now true !
}
You can add a user-defined deduction guide : 您可以添加用户定义的扣除指南 :
item(const char *) -> item<std::string>;
With this deduction guide, a
will be deduced to be item<std::string>
. 通过此推导指南,
a
将推导为item<std::string>
。
No, you can't directly make the typeid of two templated objects using different template arguements be the same. 不,您不能使用不同的模板参数直接使两个模板化对象的typeid相同。
But to achieve your end goal you can use a factory like pattern. 但是要实现最终目标,您可以使用类似工厂的模式。 It could look something like this:
它可能看起来像这样:
template<typename T, typename R = T>
item<R> make_item(T&& t)
{
return item<T>(std::forward<T>(t));
}
// Specialization for const char *
template<>
item<std::string> make_item(const char *&& str)
{
return item<std::string>(str);
}
The downside with this approach is that you'll need to construct all of your objects with this factory. 这种方法的缺点是您需要使用该工厂构造所有对象。 And if you have a lot of exceptions you'll need to make a specialization for each exception.
而且如果您有很多异常,则需要对每个异常进行专门化处理。
This is more a guess than an answer, but I'd say no. 这更多的是猜测,而不是答案,但我会拒绝。 Templates are expanded at compile time, so because you are creating an
模板会在编译时进行扩展,因此,因为您正在创建
item<const char*>
and an 和
item<std::string>
then the code that gets expanded looks something like 然后扩展的代码看起来像
class item1
{
bar<const char*> thing;
// other attributes ...
public:
item(const char* t) : thing(t) {}
// other constructors ...
bar<const char*> what() const
{
return thing;
}
};
class item2
{
bar<std::string> thing;
// other attributes ...
public:
item(std::string t) : thing(t) {}
// other constructors ...
bar<std::string> what() const
{
return thing;
}
};
(More or less; they wouldn't actually be called item1 and item2) (或多或少;它们实际上不会被称为item1和item2)
How you chose to evaluate these two types is up to you, but to the compiler they are in fact two different types. 如何选择对这两种类型求值取决于您,但是对于编译器而言,它们实际上是两种不同的类型。
Ok, I'd never seen or used std::conditional before so I wasn't sure what that was doing, but after reading up on it and playing around with your code I did get it to "work" by using 好的,我之前从未看过或使用过std :: conditional,所以我不确定这样做是什么,但是在仔细阅读并试过您的代码后,我确实通过使用来使其“工作”
bar<T>
as the template type. 作为模板类型。 So instead of
所以代替
auto a = item<const char*>("const char *");
auto b = item<string>(string("string"));
I did 我做了
auto a = item<bar<const char*>>("const char *");
auto b = item<bar<string>>(string("string"));
The thing is you need the template type to be the same in both cases, meaning the type needs to resolve to std::string before the template gets expanded. 问题是两种情况下您都需要模板类型相同,这意味着在扩展模板之前,该类型需要解析为std :: string。 As long as you use your conditional, you can define any type.
只要使用条件,就可以定义任何类型。
auto c = item<bar<int>>(5);
Not sure that's a good solution (which is why I said "work"), but see my other answer about the class types actually being different. 不确定这是否是一个好的解决方案(这就是为什么我说“工作”的原因),但请参阅有关类类型实际上有所不同的其他答案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.