C++ Templates

【C++ Templates(6)】移动语义与enable_if

2018-04-16  本文已影响162人  downdemo

完美转发

#include <utility>
#include <iostream>

class X {
    ...
};

void g(X&) {
    std::cout << "g() for variable\n";
}

void g(X const&) {
    std::cout << "g() for constant\n";
}
void g(X&&) {
    std::cout << "g() for movable object\n";
}

// let f() forward argument val to g():
void f(X& val) {
    g(val); // val is non-const lvalue => calls g(X&)
}

void f(X const& val) {
    g(val); // val is const lvalue => calls g(X const&)
}

void f(X&& val) {
    g(std::move(val)); // val is non-const lvalue => needs
    std::move() to call g(X&&)
}

int main()
{
    X v; // create variable
    X const c; // create constant
    f(v); // f() for nonconstant object calls f(X&) => calls g(X&)
    f(c); // f() for constant object calls f(X const&) => calls g(X const&)
    f(X()); // f() for temporary calls f(X&&) => calls g(X&&)
    f(std::move(v)); // f() for movable variable calls f(X&&) => calls g(X&&)
}
template<typename T>
void f (T val) {
    g(T);
}
template<typename T>
void f (T&& val) {
    g(std::forward<T>(val)); // perfect forward val to g()
}
#include <utility>
#include <iostream>

class X {
    ...
};

void g(X&) {
    std::cout << "g() for variable\n";
}

void g(X const&) {
    std::cout << "g() for constant\n";
}

void g(X&&) {
    std::cout << "g() for movable object\n";
}

// let f() perfect forward argument val to g()
template<typename T>
void f (T&& val) {
    g(std::forward<T>(val)); // call the right g() for any passed argument val
}

int main()
{
    X v; // create variable
    X const c; // create constant
    f(v); // f() for variable calls f(X&) => calls g(X&)
    f(c); // f() for constant calls f(X const&) => calls g(X const&)
    f(X()); // f() for temporary calls f(X&&) => calls g(X&&)
    f(std::move(v)); // f() for move-enabled variable calls f(X&&) => calls g(X&&)
}

特殊成员函数模板

#include <utility>
#include <string>
#include <iostream>

class Person
{
private:
    std::string name;
public:
    // constructor for passed initial name:
    explicit Person(std::string const& n) : name(n) {
        std::cout << "copying string-CONSTR for '" << name << "'\n";
    }
    explicit Person(std::string&& n) : name(std::move(n)) {
        std::cout << "moving string-CONSTR for '" << name << "'\n";
    }
    // copy and move constructor:
    Person (Person const& p) : name(p.name) {
        std::cout << "COPY-CONSTR Person '" << name << "'\n";
    }
    Person (Person&& p) : name(std::move(p.name)) {
        std::cout << "MOVE-CONSTR Person '" << name << "'\n";
    }
};

int main()
{
    std::string s = "sname";
    Person p1(s);              // init with string object => calls copying string-CONSTR
    Person p2("tmp");          // init with string literal => calls moving string-CONSTR
    Person p3(p1);             // copy Person => calls COPY-CONSTR
    Person p4(std::move(p1));  // move Person => calls MOVE-CONST
}
#include <utility>
#include <string>
#include <iostream>

class Person
{
  private:
    std::string name;
  public:
    // generic constructor for passed initial name:
    template<typename STR>
    explicit Person(STR&& n) : name(std::forward<STR>(n)) {
        std::cout << "TMPL-CONSTR for '" << name << "'\n";
    }

    // copy and move constructor:
    Person (Person const& p) : name(p.name) {
        std::cout << "COPY-CONSTR Person '" << name << "'\n";
    }
    Person (Person&& p) : name(std::move(p.name)) {
        std::cout << "MOVE-CONSTR Person '" << name << "'\n";
    }
};
std::string s = "sname";
Person p1(s); // init with string object => calls TMPL-CONSTR
Person p2("tmp"); //init with string literal => calls TMPL-CONSTR
Person p3(p1); // ERROR
Person p4(std::move(p1)); // OK: move Person => calls MOVECONST
Person const p2c("ctmp"); //init constant object with string literal
Person p3c(p2c); // OK: copy constant Person => calls COPY-CONSTR
template<typename STR>
Person(STR&& n)
// 优于
Person (Person const& p)
Person (Person& p)

使用enable_if<>禁用成员模板

template<typename T>
typename std::enable_if<(sizeof(T) > 4)>::type
foo() {
}
void foo() {
}
template<typename T>
std::enable_if_t<(sizeof(T) > 4)>
foo() {
}
template<typename T>
std::enable_if_t<(sizeof(T) > 4), T>
foo() {
    return T();
}
MyType foo();
template<typename T,
typename = std::enable_if_t<(sizeof(T) > 4)>>
void foo() {
}
template<typename T,
typename = void>
void foo() {
}
template<typename T>
using EnableIfSizeGreater4 = std::enable_if_t<(sizeof(T) > 4)>;

template<typename T,
    typename = EnableIfSizeGreater4<T>>
void foo() {
}

使用enable_if<>

template<typename STR>
Person(STR&& n);
template<typename STR,
    typename = std::enable_if_t<std::is_convertible_v<STR, std::string>>>
Person(STR&& n);
template<typename STR,
    typename = void>
Person(STR&& n);
template<typename T>
using EnableIfString = std::enable_if_t<std::is_convertible_v<T, std::string>>;
...
template<typename STR, typename = EnableIfString<STR>>
Person(STR&& n);
// basics/specialmemtmpl3.hpp

#include <utility>
#include <string>
#include <iostream>
#include <type_traits>

template<typename T>
using EnableIfString =
    std::enable_if_t<std::is_convertible_v<T,std::string>>;

class Person
{
private:
    std::string name;
public:
    // generic constructor for passed initial name:
    template<typename STR, typename = EnableIfString<STR>>
    explicit Person(STR&& n)
     : name(std::forward<STR>(n)) {
        std::cout << "TMPL-CONSTR for '" << name << "'\n";
    }
    // copy and move constructor:
    Person (Person const& p) : name(p.name) {
        std::cout << "COPY-CONSTR Person '" << name << "'\n";
    }
    Person (Person&& p) : name(std::move(p.name)) {
        std::cout << "MOVE-CONSTR Person '" << name << "'\n";
    }
};
#include "specialmemtmpl3.hpp"
int main()
{
    std::string s = "sname";
    Person p1(s); // init with string object => calls TMPL-CONSTR
    Person p2("tmp"); // init with string literal => calls TMPL-CONSTR
    Person p3(p1); // OK => calls COPY-CONSTR
    Person p4(std::move(p1)); // OK => calls MOVE-CONST
}
template<typename T>
using EnableIfString =
    std::enable_if_t<std::is_convertible<T,std::string>::value>;
template<typename T>
using EnableIfString
    = typename std::enable_if<std::is_convertible<T,std::string>::value>::type;
template<typename T>
using EnableIfString =
    std::enable_if_t<std::is_constructible_v<std::string,T>>;
class C {
public:
    template<typename T>
    C (T const&) {
        std::cout << "tmpl copy constructor\n";
    }
    ...
};

C x;
C y{x}; // still uses the predefined copy constructor (not the member template)
class C
{
public:
    ...
    // user-define the predefined copy constructor as deleted
    // (with conversion to volatile to enable better matches)
    C(C const volatile&) = delete;
    // implement copy constructor template with better match:
    template<typename T>
    C (T const&) {
        std::cout << "tmpl copy constructor\n";
    }
    ...
};

C x;
C y{x}; // uses the member template
template<typename T>
class C
{
public:
    ...
    // user-define the predefined copy constructor as deleted
    // (with conversion to volatile to enable better matches)
    C(C const volatile&) = delete;
    // if T is no integral type, provide copy constructor template with better match:
    template<typename U, typename =
        std::enable_if_t<!std::is_integral<U>::value>>
    C (C<U> const&) {
        ...
    }
    ...
};

使用concept简化enable_if<>表达式

template<typename STR>
requires std::is_convertible_v<STR,std::string>
Person(STR&& n) : name(std::forward<STR>(n)) {
    ...
}
template<typename T>
concept ConvertibleToString =
    std::is_convertible_v<T,std::string>;
template<typename STR>
requires ConvertibleToString<STR>
Person(STR&& n) : name(std::forward<STR>(n)) {
    ...
}
template<ConvertibleToString STR>
Person(STR&& n) : name(std::forward<STR>(n)) {
    ...
}
上一篇 下一篇

猜你喜欢

热点阅读