模板小计

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\n"; }

template<class C>
void test(C c, int)
    { std::cout << "int\n"; }

struct X {
    int f() { return 42; }
    double g(int) { return 3.14; }
};

int main()
{
    X x;
    test(x, &X::f);  // ok - outputs "member function\n"
    // test(x, &X::g);  // CT error - g needs an argument
    test(x, 99);   // ok - outputs "int\n"
}

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

在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\n",has_no_destroy<A>::value);
    printf("%d\n",has_no_destroy<B>::value);
    printf("%d\n",has_no_destroy<C>::value);
    printf("%d\n",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 ...

待续

 

posted @ 2019-07-18 19:19  秋来叶黄  阅读(300)  评论(0编辑  收藏  举报