如果方法不进行类型检查,为什么C++模板匹配?

Joe*_*van 8 c++ templates sfinae c++11

以下代码无法编译,因为struct A不支持--运算符.

struct A {};

struct B {
  void Run() {}
  A& Dec(A& a) { return --a; }
};

int main(int argc, char** argv) {
  B b;
  b.Run();
}
Run Code Online (Sandbox Code Playgroud)

与此代码相同.

struct A {};

template <class T>
struct B {
  void Run() {}
  A& Dec(A& a) { return --a; }
};

int main(int argc, char** argv) {
  B<A> b;
  b.Run();
}
Run Code Online (Sandbox Code Playgroud)

那么为什么要编译(在C++ 11中)?

struct A {};

template <class T>
struct B {
  void Run() {}
  T& Dec(T& a) { return --a; }
};

int main(int argc, char** argv) {
  B<A> b;
  b.Run();
}
Run Code Online (Sandbox Code Playgroud)

实例化模板似乎不会自动实例化模板中依赖于类型参数进行类型检查的未使用方法,这意味着即使某些方法没有,模板也会匹配.这令人失望,因为我希望使用SFINAE来检测各种方法和运算符对类型的适用性,但是如果模板替换成功,即使对方法的调用是编译时错误,该技术也行不通.

Yak*_*ont 5

(由C++委员会决定)模板类的方法只有在使用它们时才会实例化它们的实体.

这样可以更容易地编写一些C++代码,代价是使用时出现硬错误.

作为一个例子,std::vector使用它std::vector::operator<; 如果你没有<电话,那就是错误.如果你这样做,称之为有效.

更现代的C++会鼓励SFINAE禁用它,以便您可以检测是否<安全,但该技术在std::vector设计时未被使用.你可以看到这种技术的使用的演变std::function,它从贪婪地消耗了通用构造函数中的几乎任何东西,到只在C++ 11和C++ 14之间工作的构造函数被考虑用于重载解析.

如果你想要SFINAE,你就不能依赖这样的代码体.为了减轻编译器的负担,编译器只需要在进行SFINAE测试时检查声明而不是函数的定义.

部分原因是SFINAE表达很难; 在整个身体上更难.编译器必须推测性地编译函数体,命中错误,然后退回到"nope,nothing is done"状态.

函数体中的错误始终是硬错误.在当前版本的C++中无法避免这种情况.

现在,您可以编写决定是否存在错误的函数,但实际上没有错误,然后使用它们的主体来确定其他代码是否会出错.例如:

template<class T>
auto foo() {
  constexpr if(sizeof(T)<4) {
    return std::true_type{};
  } else {
    return std::false_type{};
}
Run Code Online (Sandbox Code Playgroud)

你可以foo<char>()在某个地方使用某些SFINAE,它的truefalse-ness可以使另一个过载替换失败或不同.

请注意,错误(如果有)仍然发生函数体(foo此处)之外.