C ++实现is_convertible&引用

时间:2016-04-05 01:54:35

标签: c++ templates typetraits

我创建了以下特征模板类来确定是否可以将一种类型转换为另一种类型:

namespace Detail
{
    struct IsConvertible
    {
        template <typename _Type, typename _OtherType>
        static void convert(_OtherType);
        template <typename _Type, typename _OtherType>
        static auto test(_Type&&, _OtherType&&) -> decltype(convert<_Type, _OtherType>(std::declval<_Type>()), std::true_type());
        static auto test(...) -> decltype(std::false_type());
    };
}
template <typename _Type, typename _OtherType>
struct IsConvertible : public decltype(Detail::IsConvertible::test(std::declval<_Type>(), std::declval<_OtherType>()))
{};

然后尝试了以下测试用例:

void func1(int& i) {

}
void func2(int i) {

}
#include <iostream>
using namespace std;
int main()
{

    cout << IsConvertible<int, long>::value;
    cout << IsConvertible<int, float>::value;
    cout << IsConvertible<int, int*>::value;
    cout << IsConvertible<float, double>::value;
    cout << IsConvertible<int&, int>::value;
    cout << IsConvertible<int, int&>::value;

    int i = 2;
    int& ir = i;
    int* ip = &i;

    func1(i);
    func2(ir);

    return 0;
}

我正在使用MSVC 2015.这些测试的输出是:

110110

正确处理数字促销活动&amp;转化,但参考转化有问题。这个实现有什么问题?

修改:我更新了测试版&amp;将我的结果与std :: is_convertible进行了比较。结果完全相同。

2 个答案:

答案 0 :(得分:1)

首先,你的问题有点不清楚。可以说“X&amp;”如果类型可以复制或移动构造,则可以转换为“X”;也可以说它们是不同的类型,不能转换。

鉴于您的一个测试用例导致intlong之间的“可转换”结果,我将假设您的预期结果是“松散的”。也就是说,如果有任何方式可以从foo转换为bar,那么结果是肯定的,您的问题是:

cout << IsConvertible<int, int&>::value;

给出“假”的结果,但你希望:好吧,如果我有一个int,我应该能够把它变成int &,当然,不是问题

这是我的假设。那么,让我们看看这种转换在哪些方面脱轨:

convert<_Type, _OtherType>(std::declval<_Type>()),

您在这里使用SFINAE。 _Typeint_OtherTypeint &

顺便说一句,您不应该使用以下划线开头并后跟大写字母的标识符。它们保留供C ++库使用。

您的convert模板是:

template <typename _Type, typename _OtherType>
    static void convert(_OtherType);

因此,对于此模板实例化,您将实例化

static void convert(int &);

您的调用是:

convert(std::declval<int>());

std::declval<int>的实例化将返回一个int,函数调用std::declval<int>()的结果将是一个右值。

此函数调用失败,因为int rvalue无法绑定到int &函数调用参数,只能绑定到const int &参数。换句话说,用:

int declval();

void convert(int &);

以下失败:

convert(declval());

这就是为什么你的SFINAE替换失败,转换失败的原因。

我将建议一种稍微替代的方法:

template<typename Type> class TypeValue {

public:

    static TypeValue param;
};

然后,不要使用std::declval,而是使用以下内容:

convert<_Type, _OtherType>(TypeValue<_Type>::param),

答案 1 :(得分:0)

您不能重载引用或指针运算符。 你可以输入id(x).name()。这将为您提供x的类型名称。 您可以创建一个像这样的容器:

    #include <iostream>
    #include <map>
    #include <string>

    using namespace std;

    #define TYPES_CNV_COUNT 1
    typedef void**(*__CONVERT_FUNC__)(void*);

    void** cnv_str(void* _elm){
        void** a = new void*[2];
        a[0] = new string((const char*)_elm);
        a[1] = _elm;
        return (a);
    }
    class Converter{
    public:
        Converter(){
            m_mTypes.insert(pair<string, int>(typeid(string).name(), 0));
            m_mConvertFunc[0].insert(pair<string, __CONVERT_FUNC__>(typeid(const char*).name(), (__CONVERT_FUNC__)&cnv_str));
        }
        int  isCnv(const string& _nameElm1, const string& _nameElm2)const{
            map<string, int>::const_iterator itCnv = m_mTypes.find(_nameElm1);
            map<string, __CONVERT_FUNC__>::const_iterator itCnvFunc;
            int index;
            if (itCnv == m_mTypes.end())
                return (-1);
            itCnvFunc = m_mConvertFunc[index = itCnv->second].find(_nameElm2);
            if (itCnvFunc == m_mConvertFunc[index].end())
                return (-1);
            return (itCnv->second);
        }
        void** cnv(const string& _nameElm1, const string& _nameElm2,void* _elm)const{
            int index = isCnv(_nameElm1, _nameElm2);

            if (index > -1)
            {
                map<string, __CONVERT_FUNC__>::const_iterator itCnvFunc = m_mConvertFunc[index].find(_nameElm2);
                return ((*(itCnvFunc->second))(_elm));
            }
            return (NULL);
        }
    private:
        map<string, int>               m_mTypes;
        map<string, __CONVERT_FUNC__> m_mConvertFunc[TYPES_CNV_COUNT];
    };

    int main(void){
        Converter cnv;
        string hello = "hello";
        const char* world = "world";
        void** e;

        if (cnv.isCnv(string(typeid(hello).name()), string(typeid(world).name())) > -1){
            cout << "convertable!\n";
        }
        else{
            cout << "not convertable!\n";
        }

        if ((e = cnv.cnv(string(typeid(hello).name()), string(typeid(world).name()),(void*)world)) != NULL){
            cout << "type: " << typeid(*(string*)e[0]).name() << endl;
            cout << "data: " << ((string*)e[0])->c_str() << endl;
        }
        else{
            cout << "failed to convert" << endl;
        }

        system("pause");
        delete e[0];
        delete[] e;
        return (0);
    }