与SFINAE中的硬错误混淆

Cur*_*ous 15 c++ templates sfinae c++17

关于以下代码(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>{});
}
Run Code Online (Sandbox Code Playgroud)

上面的代码以错误退出,因为实例化Foo<T>::impl()导致硬错误并且在SFINAE上下文中不可用.但奇怪的是,当你切换in 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>()))>;
Run Code Online (Sandbox Code Playgroud)

现在的问题是

  1. 为什么代码最初没有编译?实例化Foo<T>::impl()是在替换的上下文中,所以应该有效吗?
  2. 替换foo_name::foo(T)第一个参数代替void_t将使其编译(参见https://wandbox.org/permlink/g3NaPFZxdUPBS7oj),为什么?如何添加一个额外的间接层使情况不同?
  3. 为什么顺序void_t有所不同,编译器是否会使类型包中的表达式短路?

eca*_*mur 7

1)和2)有相同的答案; SFINAE不适用于返回类型推导,因为函数体不在直接上下文中:

10 - 当实例化定义时,即使函数体包含return具有非类型相关操作数的语句,也会在其声明类型中使用占位符的函数模板返回类型推导.[ 注意:因此,任何使用函数模板的特化都会导致隐式实例化.此实例化产生的任何错误都不在函数类型的直接上下文中,并且可能导致程序格式错误(17.8.2).- 结束说明 ]

3)是一个更有趣的问题 ; 短路是有意的,并由[temp.deduct]保证:

7 - [...]替换以词汇顺序进行 ,并在遇到导致扣除失败的条件时停止.

这种短路适用于gcc,clang和ICC,但遗憾的是MSVC(截至2009年第9版RTW)出错了,例如:

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);
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

366 次

最近记录:

8 年,5 月 前