关于以下代码(为方便起见,下面转载https://wandbox.org/permlink/nhx4pheijpTF1ohf)
#include <type_traits>
#include <utility>
namespace foo_name {
template <typename T>
void foo();
template <>
void foo<int>();
template <typename T>
struct c_size;
template <>
struct c_size<int> : public std::integral_constant<int, 1> {};
} // namespace foo_name
template <typename Type>
class Foo {
public:
template <typename T>
static decltype(auto) impl(T&& t) {
using foo_name::foo;
return foo(std::forward<T>(t));
}
};
class Something {};
template <typename Type, typename T = std::decay_t<Type>>
using EnableIfHasFoo = std::void_t<
decltype(Foo<T>::impl(std::declval<T>())),
decltype(foo_name::c_size<Type>::value)>;
template <typename Type, typename = std::void_t<>>
class Test {};
template <typename Type>
class Test<Type, EnableIfHasFoo<Type>> {};
int main() {
static_cast<void>(Test<Something>{});
}
上面的代码退出并出现错误,因为Foo<T>::impl()
的实例化会导致硬错误,并且在SFINAE上下文中不可用。但奇怪的是,当你在void_t
EnableIfHasFoo
中切换事物的顺序(到下面的https://wandbox.org/permlink/at1KkeCraNwHGmUI)时,它会编译
template <typename Type, typename T = std::decay_t<Type>>
using EnableIfHasFoo = std::void_t<
decltype(foo_name::c_size<Type>::value),
decltype(Foo<T>::impl(std::declval<T>()))>;
现在问题是
Foo<T>::impl()
的实例化是在替换的上下文中,所以应该有效吗?foo_name::foo(T)
替换为void_t
的第一个参数将使其编译(参见https://wandbox.org/permlink/g3NaPFZxdUPBS7oj),为什么?如何添加一个额外的间接层会使情况不同?void_t
中的顺序有所不同,编译器是否会使类型包中的表达式短路?答案 0 :(得分:7)
1)和2)有相同的答案; SFINAE does not work return type deduction not in immediate context,因为函数的主体是a more interesting question:
10 - 函数模板的返回类型推导在其声明的类型中具有占位符 即使函数体包含具有非类型相关的
return
语句,也会实例化定义 操作数。 [注意:因此,任何使用函数模板的特化都会导致隐式 实例。此实例化产生的任何错误都不在函数的直接上下文中 输入并导致程序格式不正确(17.8.2)。 - 结束记录]
3)是[temp.deduct];短路是故意的,由for example保证:
7 - [...]替换进行 以词汇顺序,并在遇到导致演绎失败的条件时停止。
这种短路适用于gcc,clang和ICC,但遗憾的是MSVC(截至2017年第9版RTW)出错了,System.Web.Helpers.Crypto.VerifyHashedPassword
:
template<class T> auto f(T t) -> decltype(t.spork) { return t.spork; }
template<class T> auto g(T t) { return t.spork; }
int x(...);
template<class...> using V = void;
template<class T> auto x(T t) -> V<decltype(f(t)), decltype(g(t))> {}
int a = x(0);