zoukankan      html  css  js  c++  java
  • 模板小计

    1.

    decltype 返回一个数据的类型

    decltype(1)就相当于int

    decltype(1) a;->int a;//这是等价的

    decltype(23.11) b;->double b;//这是等价的

    std::declvaldecltype一起使用,作用就是调用一个结构体或是类的方法,但是不用实例化,直接用类名调用即可

    不可以单独使用

    struct Test
    {
        int fun()
        {
            return 453;
        }
    };
    decltype(std::declval<Test>().fun()) a = 1;
    //上面等价于
    int a = 1;

    std::declval<Test>().fun()就是调用Test中的fun函数,可以避免实例化一个Test

    2.0

    error C2665: 'std::to_string': none of the 9 overloads could convert all the argument types

    while trying to match the argument list '(const T)'

    编写模板编译的时候如果报上面的错误,是因为调用了std::to_string()的方法,你的模板在展开的时候,发现你传入的T参数是一个std::to_string()无法调用的类型。如下面红色部分。

    string a;
    SetValue(a);
    void SetValue(const T& svalue) { string repstr; if (std::is_arithmetic<T>::value) { repstr = std::to_string(svalue); } else { repstr = "'" + svalue + "'"; } for (auto iter = m_sqlstr.begin(); iter != m_sqlstr.end(); iter++) { if (*iter == '?') { m_sqlstr.replace(iter, iter + 1, svalue); break; } } }

    有一种修改就是把这个不符合模板里面调用方法的类型重载出来

    void SetValue(const string& svalue)
    {
        string repstr;
        repstr = "'" + svalue + "'";
        for (auto iter = m_sqlstr.begin(); iter != m_sqlstr.end(); iter++)
        {
            if (*iter == '?')
            {
                m_sqlstr.replace(iter, iter + 1, svalue);
                break;
            }
        }
    }
    template<typename T>
    void SetValue(const T& svalue)
    {
        string repstr;
        if (std::is_arithmetic<T>::value)
        {
            repstr = std::to_string(svalue);
        }
        else
        {
            repstr = "'" + svalue + "'";
        }
        for (auto iter = m_sqlstr.begin(); iter != m_sqlstr.end(); iter++)
        {
            if (*iter == '?')
            {
                m_sqlstr.replace(iter, iter + 1, svalue);
                break;
            }
        }
    }

    上面红色的就是增加的新的方法,避免了std::to_string中不符合要求的类型

    2.1

    引用 https://stackoverflow.com/questions/16591519/argument-deduction-with-template-member-function-and-non-template-overload

    template< class T >
    void add_to_header( const std::string &key, const T &val )
    {
        add_to_header( key, std::to_string( val ) );
    }
    
    virtual void add_to_header( const std::string &key, const std::string &val );

    作者碰到了一个问题,如下

    instance.add_to_header( "Example1", 4 ); // successful
    instance.add_to_header( "Example2", std::string( "str val" ) ); // successful
    instance.add_to_header( "Example3", "Not fun" ); // error - none of the 9 overloads could convert all the argument types

    原因和上面一样:

    第一行调用是(const char*, int),匹配的是模板,没问题

    第二行调用是(const char*, string),匹配的是函数,没问题

    第三行调用是(const char*, const char*),因为第二个参数const char*更能直接匹配模板的T,函数的const string& val则需要隐式转换,所以匹配的是模板。那么模板里面调用std::to_string(const char*)肯定是不对的,所以报错了。

    如果想解决这个问题,可以在定义一个函数如下,把(const char*, const char*)从模板中抽离出来

    void add_to_header(const char* key, const char* val);

    提问下面有人提出了更巧妙的方法

    void add_to_header(const std::string &key, const std::string &val);
    
    template<typename T> auto add_to_header(const std::string &key, const T &val) -> decltype(std::to_string(val), void())
    {
        add_to_header(key, std::to_string(val));
    }

    上面定义了一个模板,然后通过c++的Substitution failure is not an error (SFINAE)技术来区分使用哪种调用。

    如果调用了(const char*, const char*)那么模板在展开时,由于std::to_string(val)就会报错,那么模板就不会展开,那么就会调用上面的函数.

    但是第二个方法我这边没有编译通过。

    2.3

    Substitution failure is not an error (SFINAE)

    参考 https://stackoverflow.com/questions/31017032/decltype-with-two-parameters-decltypea-b-for-function-return-type

    上面讲到了这个技术,这个具体是用来做什么的呢,翻译过来就是展开的时候的失败不是错误。

    template<class C, class F>
    auto test(C c, F f) -> decltype((void)(c.*f)(), void())
        { std::cout << "member function
    "; }
    
    template<class C>
    void test(C c, int)
        { std::cout << "int
    "; }
    
    struct X {
        int f() { return 42; }
        double g(int) { return 3.14; }
    };
    
    int main()
    {
        X x;
        test(x, &X::f);  // ok - outputs "member function
    "
        // test(x, &X::g);  // CT error - g needs an argument
        test(x, 99);   // ok - outputs "int
    "
    }

    这个例子很清晰的介绍了这种技术

    在man中第一个调用中,传入了x和类X的函数指针,那么第一个模板是可以正常展开的,所以就调用了第一个模板

    第二行调用,发现没有匹配的模板,就会报错

    第三行调用发现第一个模板无法匹配,因为99是个整数,没有f()函数,所以展开失败,根据这个技术,失败不是错误,所以继续寻找下一个模板,然后发现可以匹配,那么就调用了第二个模板

    参考 https://www.jianshu.com/p/45a2410d4085

    long multiply(int i, int j) { return i * j; }
    
    template <class T>
    typename T::multiplication_result multiply(T t1, T t2)
    {
        return t1 * t2;
    }
    int main(void)
    {
        multiply(4,5);
    }

    模板和宏基本一样,不检测类型,在编译的时候才展开,上面的例子就是

    multiplication_result是一个非法的参数,所以main调用的时候展开失败,那么就调用了上面的函数

    template<typename T>
    struct has_no_destroy {
        template<typename C>
        static char test(decltype(&C::no_destroy));
    
    
        template<typename C>
        static int32_t test(...);
    
        const static bool value = sizeof(test<T>(0)) == 1;
    };
    // 其作用就是用来判断是否有 no_destroy 函数
    
    struct A {
    
    };
    
    struct B {
        void no_destroy(){}
    };
    struct C {
        int no_destroy;
    };
    
    struct D : B {
    
    };
    
    void testNoDestroy() {
        printf("%d
    ",has_no_destroy<A>::value);
        printf("%d
    ",has_no_destroy<B>::value);
        printf("%d
    ",has_no_destroy<C>::value);
        printf("%d
    ",has_no_destroy<D>::value);
    }

    作者提供了另外一个muduo的例子(muduo作者代码中介绍的是源于 http://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions 查资料是发现维基百科中介绍的也很详细 并且与stackoverflow中例子类似https://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error),A传入到结构体中,匹配模板的时候发现没有no_destroy函数,所以调用的是返回值为int32_t的方法,所以计算size_of不是一个字节,所以是false,其他几个都有这个函数,就调用的返回值是char的方法,所以是true

    但是test两个函数都没有实现,是没问题的,我实现了发现也并没有调用

    3.

    c++模板(template)中auto和->(箭头符号)的作用

    参考 https://stackoverflow.com/questions/22514855/arrow-operator-in-function-heading

    template <typename T, typename T1> auto compose(T a, T1 b) -> int
    {
        return a + b;
    }

    就是返回类型是不定的,所以前面是auto,然后具体返回类型是什么,由->后面指定,比如上面的代码,返回一个int。

    如果是上面使用,还不如把auto直接换成int,但是还可以增加新的用法。

    template <typename T, typename T1> auto compose(T a, T1 b) -> decltype(a+b)
    {
        return a + b;
    }

    这样就是根据a+b的类型返回,上面讲了decltype是获取这个数据的类型。

    如果是这个例子,这么麻烦的用法与直接用T定义返回值没有什么优势,但是decltype里面还可以加方法,这样的话就可以实现更复杂的用法,仅仅是用T来做为返回值是无法达到的。

    C++14后增加了更简单的用法可以把->后面的省略掉。

    template <typename T, typename T1> auto compose(T a, T1 b)
    {
        return a + b;
    }

    4.

    在调用模板的方法时,有时候需要指定增加tamplate关键字,因为有时候会引起起义导致程序不知道应该怎么运行。

    参考 https://www.zhihu.com/question/37990298

    template<class T>
    int f(T& x)
    {
         return x.template convert<3>(pi);
     }

    上面的代码如果没有增加template就有可能被理解为

    return ((x.convert) < 3) > (pi);

    5.

    template using的作用就是用模板定义别名。下面的例子中,两句话是等价的

    template<typename T> using tp = T * ;
    tp<int> a;
    int* a;

    6.

     std::is_same::value

    template <class T, class U> struct is_same;

    这个模板的作用是用来判断两个类型T和U是不是同样的变量。

    is_same<int, int>::value就是true,这样可以在模板中对特殊类型进行判断做特殊的处理

    7.

    形参包 Parameter pack ...

    待续

  • 相关阅读:
    Java多线程学习(吐血超具体总结)
    java.lang.Integer can not be cast to java.lang.Long
    【转】随身HiFi 安卓OTG功能在音频上的妙用
    【转】锋狂百科:手机也能接外设 OTG技术详解
    【转】用串口登录Beaglebone Black、用usb共享电脑网络、内核模块的本地编译
    【转】Beagleboard:BeagleBoneBlack
    【转】
    【转】Beaglebone Black
    【转】你应该知道的 10 个 VirtualBox 技巧与高级特性
    如何把SKYPE的发送消息由enter改为ctrl+enter?
  • 原文地址:https://www.cnblogs.com/studywithallofyou/p/11209449.html
Copyright © 2011-2022 走看看