简体   繁体   English

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM