Pau*_*ulo 3 c++ static-assert sfinae
请考虑以下 C++14 代码:
#include <type_traits>
template<typename T>
class Bar {
static_assert(std::is_destructible<T>::value, "T must be destructible");
};
template<typename T>
void foo(Bar<T> const &) {}
template<typename T>
void foo(T const &) {}
class Product {
public:
static Product *createProduct() { return new Product{}; }
static void destroyProduct(Product *product) { delete product; }
private:
Product() = default;
~Product() = default;
};
int main()
{
Product* p = Product::createProduct();
foo(*p); // call 1: works fine
foo<Product>(*p); // call 2: fails to compile
Product::destroyProduct(p);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
以及来自 clang 的错误消息:
error: static_assert failed due to requirement 'std::is_destructible<Product>::value' "T must be destructible"
static_assert(std::is_destructible<T>::value, "T must be destructible");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: in instantiation of template class 'Bar<Product>' requested here
foo<Product>(*p); // call 2: fails to compile
^
note: while substituting deduced template arguments into function template 'foo' [with T = Product]
foo<Product>(*p); // call 2: fails to compile
^
1 error generated.
Run Code Online (Sandbox Code Playgroud)
我的理解是 和call 1都call 2应该编译良好,但call 2不仅在 clang 上编译失败,而且在 gcc 和 msvc 上编译失败。
call 1从标准的角度来看,编译成功和call 2失败是否正确?为什么?
注意:我知道我可以通过std::enable_if在第一个重载中添加 a 来解决该错误foo,但我想了解为什么call 1可以但call 2不行。
替换失败不属于有限错误。如果故障发生在“直接上下文”之外,则故障很困难并且 SFINAE 无法阻止。
当你输入foo<Product>(?),它首先创建一个重载集。该重载集包括 的两个重载foo。但是创建 foo 的第一个重载会导致static_assert直接上下文之外的失败,因此是一个硬错误。
当你输入foo(?)它还尝试创建一个过载集。两个模板均被考虑。它没有T被传入,因此它尝试T从参数中推断出 。
参数是一个Product&. T const&从 aProduct&中推导出T=Product const. Bar<T> const&从 a ...推导Product&无法推导 a T,因为Product它的任何基类都不是从 form 的模板生成的Bar<typename>。
如果没有T推导,foo(Bar<T> const&)重载将被丢弃。无法T=Product推断,因此尝试实例化时不会foo(Bar<Product> const&)发生硬错误。
简而言之,对于调用 2,您手动强制一种Bar<Product>可能性存在。对于调用 1,编译器尝试推断模板参数,但从未到达那里。
作为一个思想实验,考虑将 的声明更改Product为:
class Product: public Bar<int> {
Run Code Online (Sandbox Code Playgroud)
并使其他一切保持不变。
现在
foo(*p)
Run Code Online (Sandbox Code Playgroud)
将产生 2 个要考虑的过载 - 一个
template<class T=Product const>
void foo(Product const&)
Run Code Online (Sandbox Code Playgroud)
和一个
template<class T=int>
void foo(Bar<int> const&)
Run Code Online (Sandbox Code Playgroud)
第二个是通过查看 的基类Product并找到Bar<int>. 在一种情况下T被推论出来,在另一种情况下被推论出来。Productint
但是,当您这样做时,foo<Product>您并不是依靠推导来查找T- 您正在手动设置它。没有任何扣除可做。
| 归档时间: |
|
| 查看次数: |
105 次 |
| 最近记录: |