替换失败导致编译错误

Substitution failure causes a compile error

提问人:biubiuty 提问时间:7/19/2023 更新时间:7/20/2023 访问量:29

问:

我试图更好地了解 SFINAE,并注意到如果第一个检查非类型参数是否具有函数类型的代码被注释掉,则以下代码会产生编译错误。enable_ifg

#include <type_traits>

// Require that a function have a return type of void
template <typename T>
struct ReturnTypeHelper 
{};

template <typename R, typename ... Args>
struct ReturnTypeHelper<R(Args...)> {
    using type = R;
};

template <typename T>
struct ReturnVoid : std::is_same<typename ReturnTypeHelper<T>::type, void> 
{};

enum class moo {
    yes,
    no,
};

// First candidate
template <auto f, moo m = moo::yes>
void foo() {}

// Second candidate
// Compile error if the first enable_if is commented out
template <auto f, auto g, moo m = moo::yes,
          // Require that g be a function
          std::enable_if_t<std::is_function_v<std::remove_pointer_t<decltype(g)>>, void>* = nullptr,
          // Require that g have a return type of void
          std::enable_if_t<ReturnVoid<typename std::remove_pointer_t<decltype(g)>>::value, void>* = nullptr>
void foo() {}

void p() {}

int main()
{
    foo<p, moo::yes>();

    return 0;
}

错误是:

<source>: In instantiation of 'struct ReturnVoid<moo>':
<source>:32:101:   required by substitution of 'template<auto f, auto g, moo m, std::enable_if_t<ReturnVoid<typename std::remove_pointer<decltype (g)>::type>::value, void>* <anonymous> > void foo() [with auto f = p; auto g = moo::yes; moo m = moo::yes; std::enable_if_t<ReturnVoid<typename std::remove_pointer<decltype (g)>::type>::value, void>* <anonymous> = <missing>]'
<source>:39:21:   required from here
<source>:14:8: error: no type named 'type' in 'struct ReturnTypeHelper<moo>'
   14 | struct ReturnVoid : std::is_same<typename ReturnTypeHelper<T>::type, void>
      |        ^~~~~~~~~~
Compiler returned: 1

对于候选模板 2 和函数调用,由于不是函数类型,因此选择 的主模板,该模板没有成员。但是,如果出现此故障,为什么编译器不直接选择模板候选 1,而是报告此错误?foo<p, moo::yes>()moo::yesstruct ReturnTypeHelpertype

更新:godbolt

模板 C++17 SFINAE

评论


答:

1赞 n. m. could be an AI 7/20/2023 #1

观察:

constexpr bool a = ReturnVoid<void()>::value; // true
constexpr bool b = ReturnVoid<int()>::value; // false
constexpr bool c = ReturnVoid<int>::value; // compilation error

现在查看 SFINAE 的 cpperefertence

只有函数类型或其模板参数类型或其显式说明符(从 C++20 开始)的直接上下文中的类型和表达式中的失败才是 SFINAE 错误。如果替换类型/表达式的计算导致副作用,例如实例化某些模板专用化、生成隐式定义的成员函数等,则这些副作用中的错误将被视为硬错误。

所以这正是。您需要重写,而不是编译错误。最简单的方法是将默认值更改为ReturnVoidReturnVoid<int>::valuefalseReturnTypeHelper

template <typename T>
struct ReturnTypeHelper 
{ using type = void(); };

(没有函数可以返回,这是另一个函数)void()