类模板实例化中的类型转换

时间:2018-09-09 01:44:54

标签: c++ templates c++17

我有一个模板类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 !
}

4 个答案:

答案 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);

不确定这是一个很好的解决方案(这就是为什么我说“工作”的原因),但请参见其他有关类类型实际上不同的答案。