Skip to the content.

Deduced Context

namespace jc {

template <int N>
struct A {
  using T = int;

  void f(int) {}
};

template <int N>  // A<N>::T 是 non-deduced context,X<N>::*p 是 deduced context
void f(void (A<N>::*p)(typename A<N>::T)) {}

}  // namespace jc

int main() {
  using namespace jc;
  f(&A<0>::f);  // 由 A<N>::*p 推断 N 为 0,A<N>::T 则使用 N 变为 A<0>::T
}
namespace jc {

template <typename T>
void f(T x = 42) {}

}  // namespace jc

int main() {
  jc::f<int>();  // T = int
  jc::f();       // 错误:无法推断 T
}

特殊的推断情况

namespace jc {

struct A {
  void f(int*) const noexcept {}
};

template <typename RT, typename T, typename... Args>
void f(RT (T::*)(Args...) const) {}

}  // namespace jc

int main() {
  jc::f(&jc::A::f);  // RT = void,T = A,Args = int*
}
namespace jc {

template <typename T>
void f(T) {}

struct A {
  template <typename T>
  operator T&() {
    static T x;
    return x;
  }
};

void g(int (&)[3]) {}

}  // namespace jc

int main() {
  void (*pf)(int) = &jc::f;  // 推断为 f<int>(int)

  jc::A a;
  jc::g(a);  // a 要转为 int(&)[3],T 推断为 int[3]
}
#include <initializer_list>

namespace jc {

template <typename T>
void f(T) {}

template <typename T>
void g(std::initializer_list<T>) {}

}  // namespace jc

int main() {
  // jc::f({1, 2, 3});  // 错误:不能推断出 T 为 initializer_list
  jc::g({1, 2, 3});  // OK:T 为 int
}
namespace jc {

template <typename T, typename U>
struct A {};

template <typename T, typename... Args>
void f(const A<T, Args>&...);

template <typename... T, typename... U>
void g(const A<T, U>&...);

}  // namespace jc

int main() {
  using namespace jc;
  f(A<int, bool>{}, A<int, char>{});   // T = int, Args = [bool,char]
  g(A<int, bool>{}, A<int, char>{});   // T = [int, int], U = [bool, char]
  g(A<int, bool>{}, A<char, char>{});  // T = [int, char], U = [bool, char]
  // f(A<int, bool>{}, A<char, char>{});  // 错误,T 分别推断为 int 和 char
}
#include <utility>

namespace jc {

constexpr int g(...) { return 1; }
constexpr int g(int*) { return 2; }

template <typename T>
constexpr int f(T&& t) {
  return g(std::forward<T>(t));
}

}  // namespace jc

static_assert(jc::f(0) == 1);
static_assert(jc::g(0) == 2);
static_assert(jc::f(nullptr) == 2);
static_assert(jc::g(nullptr) == 2);

int main() {}

SFINAE(Substitution Failure Is Not An Error)

#include <vector>

namespace jc {

template <typename T, std::size_t N>
T* begin(T (&a)[N]) {
  return a;
}

template <typename Container>
typename Container::iterator begin(Container& c) {
  return c.begin();
}

}  // namespace jc

int main() {
  std::vector<int> v;
  int a[10] = {};

  jc::begin(v);  // OK:只匹配第二个,SFINAE out 第一个
  jc::begin(a);  // OK:只匹配第一个,SFINAE out 第二个
}
namespace jc {

template <typename T, typename U>
auto f(T t, U u) -> decltype(t + u) {
  return t + u;
}

void f(...) {}

template <typename T, typename U>
auto g(T t, U u) -> decltype(auto) {  // 必须实例化 t 和 u 来确定返回类型
  return t + u;  // 不是即时上下文,不会使用 SFINAE
}

void g(...) {}

struct X {};

using A = decltype(f(X{}, X{}));  // OK:A 为 void
using B = decltype(g(X{}, X{}));  // 错误:g<X, X> 的实例化非法

}  // namespace jc

int main() {}
#include <cassert>
#include <string>

namespace jc {

template <typename T>
auto size(const T& t) -> decltype(t.size(), T::size_type()) {
  return t.size();
}

}  // namespace jc

int main() {
  std::string s;
  assert(jc::size(s) == 0);
}
namespace jc {

template <typename T>
class Array {
 public:
  using iterator = T*;
};

template <typename T>
void f(typename Array<T>::iterator) {}

template <typename T>
void f(T*) {}

}  // namespace jc

int main() {
  jc::f<int&>(0);  // 错误:第一个模板实例化 Array<int&>,创建引用的指针是非法的
}
#include <cassert>
#include <iostream>
#include <sstream>
#include <string>
#include <type_traits>

namespace jc {

template <
    typename K, typename V,
    std::enable_if_t<std::is_same_v<std::decay_t<V>, bool>, void*> = nullptr>
void append(std::ostringstream& os, const K& k, const V& v) {
  os << R"(")" << k << R"(":)" << std::boolalpha << v;
}

template <typename K, typename V,
          std::enable_if_t<!std::is_same_v<std::decay_t<V>, bool> &&
                               std::is_arithmetic_v<std::decay_t<V>>,
                           void*> = nullptr>
void append(std::ostringstream& os, const K& k, const V& v) {
  os << R"(")" << k << R"(":)" << v;
}

template <
    typename K, typename V,
    std::enable_if_t<std::is_constructible_v<std::string, std::decay_t<V>>,
                     void*> = nullptr>
void append(std::ostringstream& os, const K& k, const V& v) {
  os << R"(")" << k << R"(":")" << v << R"(")";
}

void kv_string_impl(std::ostringstream& os) {}

template <typename V, typename... Args>
std::void_t<decltype(std::cout << std::declval<std::decay_t<V>>())>
kv_string_impl(std::ostringstream& os, const std::string& k, const V& v,
               const Args&... args) {
  append(os, k, v);
  if constexpr (sizeof...(args) >= 2) {
    os << ",";
  }
  kv_string_impl(os, args...);
}

template <typename... Args>
std::string kv_string(const std::string& field, const Args&... args) {
  std::ostringstream os;
  os << field << ":{";
  kv_string_impl(os, args...);
  os << "}";
  return os.str();
}

}  // namespace jc

int main() {
  std::string a{R"(data:{})"};
  std::string b{R"(data:{"name":"jc","ID":1})"};
  std::string c{R"(data:{"name":"jc","ID":1,"active":true})"};
  assert(a == jc::kv_string("data"));
  assert(b == jc::kv_string("data", "name", "jc", "ID", 1));
  assert(c == jc::kv_string("data", "name", "jc", "ID", 1, "active", true));
}

Deduction Guides

#include <vector>

namespace jc {

template <typename T>
class A {
 public:
  A(const T& val) : container_({val}) {}

 private:
  std::vector<T> container_;
};

}  // namespace jc

int main() {
  jc::A a = "downdemo";  // 错误:T 为 char[9],构造 std::vector<char[9]> 出错
}
#include <type_traits>
#include <vector>

namespace jc {

template <typename T>
class A {
 public:
  A(T val) : container_({std::move(val)}) {}

 private:
  std::vector<T> container_;
};

}  // namespace jc

int main() {
  jc::A a = "downdemo";
  static_assert(std::is_same_v<decltype(a), jc::A<const char*>>);
}
#include <string>
#include <type_traits>
#include <vector>

namespace jc {

template <typename T>
class A {
 public:
  A(const T& val) : container_({val}) {}

 private:
  std::vector<T> container_;
};

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

}  // namespace jc

int main() {
  jc::A a{"downdemo"};  // 等号初始化会出错,const char[9] 不能转为 std::string
  static_assert(std::is_same_v<decltype(a), jc::A<std::string>>);
}
#include <cassert>
#include <string>
#include <type_traits>

namespace jc {

template <typename T>
struct A {
  T x;
  std::string s;
};

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

}  // namespace jc

int main() {
  jc::A a = {"down", "demo"};
  assert(a.x == "down");
  static_assert(std::is_same_v<decltype(a.x), std::string>);
}
namespace jc {

template <typename T>
struct A {
  T x;
};

template <typename T>
A(T) -> A<T>;

}  // namespace jc

int main() {
  jc::A a1{0};     // OK
  jc::A a2 = {0};  // OK
  jc::A a3(0);   // 错误:没有初始化列表,int 不能转为 jc::A<int>
  jc::A a4 = 0;  // 错误:没有初始化列表,int 不能转为 jc::A<int>
}
namespace jc {

template <typename T, typename U>
struct A {
  A(const T&) {}
  A(T&&) {}
};

template <typename T>
A(const T&) -> A<T, T&>;

template <typename T>
explicit A(T&&) -> A<T, T>;  // 只能用于直接初始化

}  // namespace jc

int main() {
  jc::A a = 1;  // A<int, int&> a = 1;
  jc::A b{2};   // A<int, int> b{2};
}
#include <array>
#include <type_traits>

// template <typename T, typename... U>
// array(T, U...)
//     -> array<
// 		enable_if_t<(is_same_v<T, U> && ...), T>,
// 		1 + sizeof...(U)
// 	>;

int main() {
  std::array a{1, 2, 3, 4};
  static_assert(std::is_same_v<decltype(a), std::array<int, 4>>);
}
#include <string>

namespace jc {

template <typename T, typename U, typename Y = U>
struct A {
  A(T x = T{}, U y = U{}, Y z = Y{}) {}
};

}  // namespace jc

int main() {
  jc::A{1, 3.14, "hello"};  // T = int,U = double,T3 = const char*
  jc::A{1, 3.14};           // T = int,U = Y = double
  jc::A{"hi", "downdemo"};  // T = U = Y = const char*
  jc::A<std::string>{"hi", "downdemo", 42};  // 错误:只指定了 T,U 未推断
  jc::A<>{1, 3.14, 42};                      // 错误:T 和 U 都未指定
}
#include <type_traits>
#include <vector>

namespace jc {

template <typename T>
class A {
 public:
  A(const T& val) : container_({val}) {}

 private:
  std::vector<T> container_;
};

// template <typename T>
// A(const T&) -> A<T>;  // 隐式 deduction guides

}  // namespace jc

int main() {
  jc::A a1 = 0;
  jc::A a2{0};
  jc::A a3(0);
  auto a4 = jc::A{0};
  static_assert(std::is_same_v<decltype(a1), jc::A<int>>);
  static_assert(std::is_same_v<decltype(a2), jc::A<int>>);
  static_assert(std::is_same_v<decltype(a3), jc::A<int>>);
  static_assert(std::is_same_v<decltype(a4), jc::A<int>>);
}

Deduction Guides 的问题

#include <type_traits>

namespace jc {

template <typename T>
struct A {
  A(T x) {}
};

template <typename T>
A(T) -> A<T>;

}  // namespace jc

int main() {
  jc::A a1{0};
  jc::A a2{a1};  // A<int> 还是 A<A<int>>?标准委员会规定为 A<int>
  jc::A a3(a1);  // A<int> 还是 A<A<int>>?标准委员会规定为 A<int>
  static_assert(std::is_same_v<decltype(a1), jc::A<int>>);
  static_assert(std::is_same_v<decltype(a2), jc::A<int>>);
  static_assert(std::is_same_v<decltype(a3), jc::A<int>>);
}
#include <type_traits>
#include <vector>

namespace jc {

template <typename T, typename... Args>
auto f(const T& x, const Args&... args) {  // 如果 T 为 std::vector
  return std::vector{x, args...};  // 参数包是否为空将决定不同的返回类型
}

}  // namespace jc

int main() {
  using std::vector;
  vector v1{1, 2, 3};
  vector v2{v1};
  vector v3{v1, v1};
  static_assert(std::is_same_v<decltype(v1), vector<int>>);
  static_assert(std::is_same_v<decltype(v2), vector<int>>);
  static_assert(std::is_same_v<decltype(v3), vector<vector<int>>>);
  static_assert(std::is_same_v<decltype(jc::f(v1)), vector<int>>);
  static_assert(std::is_same_v<decltype(jc::f(v1, v1)), vector<vector<int>>>);
}
namespace jc {

template <typename T>
struct type_identity {
  using type = T;
};

template <typename T>
class A {
 public:
  using ArgType = typename type_identity<T>::type;
  A(ArgType) {}
};

template <typename T>
A(typename type_identity<T>::type) -> A<T>;
// 该 deduction guides 无效,因为有限定名称符 type_identity<T>::

}  // namespace jc

int main() {
  jc::A a{0};  // 错误
}
#include <type_traits>

namespace jc {

template <typename T>
struct A {
  template <typename U>
  A(U x) {}

  template <typename U>
  auto f(U x) {
    return A(x);  // 根据注入类名规则 A 是 A<T>,根据类模板实参推断 A 是 A<U>
  }
};

}  // namespace jc

int main() {
  jc::A<int> a{0};
  auto res = a.f<double>(3.14);
  static_assert(std::is_same_v<decltype(res), jc::A<int>>);
}
#include <string>
#include <type_traits>

namespace jc {

template <typename T>
struct A {
  A(const T&) {}
  A(T&&) {}
};

// template <typename T>
// A(const T&) -> A<T>;  // 隐式生成

// template <typename T>
// A(T&&) -> A<T>;  // 不会隐式生成该 deduction guides

}  // namespace jc

int main() {
  std::string s;
  jc::A a = s;  // T 推断为 std::string
  static_assert(std::is_same_v<decltype(a), jc::A<std::string>>);
  // 若指定 T&& 的 deduction guides,则 T 推断为 std::string&
}
#include <iostream>
#include <type_traits>
#include <utility>

namespace jc {

template <typename T>
struct A {};

template <typename T>
struct B {
  B(const A<T>&) { std::cout << 1 << std::endl; }
  B(A<T>&&) { std::cout << 2 << std::endl; }
};

template <typename T>
B(A<T>) -> B<T>;  // 不需要完全对应构造函数

}  // namespace jc

int main() {
  jc::A<int> a;
  jc::B{a};             // 1
  jc::B{std::move(a)};  // 2
}