Chapter2 Class Templates

Friends

template<typename> struct Point;

template<typename T> std::ostream& operator<< (std::ostream& os, const Point<T>& point);

template<typename T>
struct Point
{
  Point(T x, T y)
    : x_{x}, y_{y}
  {}

  friend std::ostream& operator<< <T>(std::ostream& os, const Point<T>& point);
private:
  T x_;
  T y_;
};

template<typename T> std::ostream& operator<< (std::ostream& os, const Point<T>& point)
{
  os << "{ x: " << point.x_ << ", y: " << point.y_ << " }";
  return os;
}

int main()
{
  Point point{ 1, 2 }; // C++17 implicit deduction guides, T is int.
  std::cout << point << '\n';
}

注意

<T>与函数名operator<<绑定,因此,这里声明了一个具体化的非成员函数模板作为friend,如果不加上<T>那么,结果是这里就将时声明一个新的非模板函数作为friend

Class Template Argument Deduction

template<typename T>
struct Foo
{
  Foo()
    : x_{}
  {}

  Foo(T x)
    : x_{x} // #2
  {}

  T x_;
};

Foo() -> Foo<int>; // #1

int main()
{
  Foo foo{}; // explicit, use #1
  Foo bar{233}; // implicit, use #2
}

注意

template<typename T, typename U>
struct Foo
{
  Foo(T x, U y)
    : x_{x}, y_{y}
  {}
  T x_;
  U y_;
};

int main()
{
  Foo<int> foo{2, 2.33}; // ERROR! U has no default type.
}
template<typename T>
struct Foo
{
  Foo(const T&)
  {
    static_assert(std::is_same_v<T, char[4]>);
  }
};

template<typename T>
struct Bar
{
  Bar(T)
  {
    static_assert(std::is_same_v<T, const char*>);
  }
};

int main()
{
  Foo foo = "foo"; // foo is `Foo<const char[4]>`
  Bar bar = "bar"; // bar is `Bar<const char*>`
}

注意

模板参数通过引用传递,那么这个参数不会decay,因此,foo的类型推断为const char[4]。而通过值传递const char[4]将会decay为const char*

为了解决类型推断中传递raw pointer带来的问题,可以显式的自定义推断规则:

#include <string>

template<typename T>
struct Foo
{
  Foo() = default;
  Foo(const T& t)
    : x_{t}
  {}

  T x_;
};

Foo(const char*) -> Foo<std::string>;

int main()
{
  Foo foo = "foo";
}

以上代码将T推断为std::string,然而,仍然不能通过编译:

c.cc: In function ‘int main()’:
c.cc:19:13: error: conversion from ‘const char [4]’ to non-scalar type ‘Foo<std::__cxx11::basic_string<char> >’ requested
   Foo foo = "foo";

这里必须将foo = "foo"更改为foo{"foo"}

Templatized Aggregate

聚合类也可以是模板类,从C++17起,可以为聚合类定义推断规则

template<typename T>
struct Foo
{
  T x;
  std::string y;
};

Foo(const char*, const char*) -> Foo<std::string>;

int main()
{
  Foo foo = {"233", "233"};
}

aggregate classes: > classes/structs with no user-provided, explicit, or inherited constructors, no private or protected nonstatic data members, no virtual functions, and no virtual, private, or protected base classes.