我有一个模板类T
,它存储各种类型的item
对象。它还在实例化/初始化时将属性附加到那些对象。
我要实现的一件特别的事情是,每当const char *
看到std::string
时,它就会视其为item
并将其存储。可以这样做,如下。
但是在类型检查中,我发现从const char *
实例化的item
与从std::string
实例化的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
}
。
item
我的问题是:是否可以对模板类item
进行任何更改,以使从const char *
实例化的item
的类型与{是从std::string
实例化的{1}}?
换句话说,我可以对模板类item
的设计进行任何更改,以使typeid(a) == typeid(b)
的结果为true吗?
谢谢!
注意:这是关于模板功能的上一个question的补充。但是我认为应该在本质上有所不同,这值得一个独立的问题。
编辑:我的目标是更改模板类item
(例如item
签名)的设计,而不是main
中的代码。假定由用户提供。我想通过不要求他们在实例化中显式提供类型item
来简化T
用户的生活。这可以通过C ++ 17模板类参数推导或某些等效的解决方法来完成。
更新:谢谢大家!特别鸣谢@xskxzr,他的一句话就能解决我的问题。使用user-defined deduction guides进行类模板参数推导,我什至不需要先前代码中的bar<T>
技术。我在下面放了更新的代码供您比较。
#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 !
}
答案 0 :(得分:4)
您可以添加user-defined deduction guide:
item(const char *) -> item<std::string>;
在此推论指南中,a
将被推导为item<std::string>
。
答案 1 :(得分:1)
不,您不能使用不同的模板争论直接使两个模板化对象的typeid相同。
但是要实现最终目标,您可以使用类似工厂的模式。看起来可能像这样:
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);
}
此方法的缺点是您需要使用此工厂构造所有对象。而且,如果您有很多异常,则需要对每个异常进行专门化处理。
答案 2 :(得分:0)
这不是猜测,而是答案,但我会拒绝。模板是在编译时扩展的,所以因为您正在创建
item<const char*>
和
item<std::string>
然后扩展的代码看起来像
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;
}
};
(或多或少;它们实际上不会被称为item1和item2)
如何选择评估这两种类型取决于您,但是对于编译器来说,它们实际上是两种不同的类型。
答案 3 :(得分:0)
好吧,我以前从没看过或使用过std :: conditional,所以我不确定这是做什么的,但是在仔细阅读并尝试了您的代码之后,我确实通过使用来使其“工作”
bar<T>
作为模板类型。因此,而不是
auto a = item<const char*>("const char *");
auto b = item<string>(string("string"));
我做到了
auto a = item<bar<const char*>>("const char *");
auto b = item<bar<string>>(string("string"));
问题是两种情况下您都需要模板类型相同,这意味着在扩展模板之前,该类型需要解析为std :: string。只要使用条件,就可以定义任何类型。
auto c = item<bar<int>>(5);
不确定这是一个很好的解决方案(这就是为什么我说“工作”的原因),但请参见其他有关类类型实际上不同的答案。