为什么使用Sfinae代码编译错误,即使有一个可以匹配的模板

发布于 2025-01-22 02:47:27 字数 1788 浏览 2 评论 0原文

代码如下。

#include <tuple>
#include <array>

template <typename T, typename Type>
struct Vec {
  using value_type = T;

  static constexpr size_t size() { return Type::size; }
};

template <size_t Size>
struct Const {
  static constexpr size_t size = Size;
};

template <class T, class Type, class = void>
struct vec_size_impl {};

template <class T, class Type>
struct vec_size_impl<T, Type, std::enable_if_t<std::is_arithmetic_v<T>>>
  : std::integral_constant<size_t, Type::size> {};

template <class T, class Type>
inline constexpr size_t Vec_size_v = vec_size_impl<T, Type>::value;

template<class T, class Type, size_t... Sizes>
// template<size_t... Sizes, class T, class Type>
  std::enable_if_t<
    ((0 + ... + Sizes) == Vec_size_v<T, Type>),
    std::tuple<Vec<T, Const<Sizes>>...>
  > split(const Vec<T, Type>&) noexcept {
      return std::make_tuple<Vec<T, Const<Sizes>>...>();
    }

template<class V, class Type>
  std::enable_if_t<
    (Vec_size_v<typename V::value_type, Type> % V::size() == 0),
    std::array<V, Vec_size_v<typename V::value_type, Type> / V::size()>
  > split(const Vec<typename V::value_type, Type>&) noexcept {
      return std::array<V, Vec_size_v<typename V::value_type, Type> / V::size()>();
    }


int main() {
  Vec<int, Const<6>> a;
  split<Vec<int, Const<2>>, Const<6>>(a);
  return 0;
}

在这里(我认为)可以匹配第二个split(),但是我仍然有一个编译错误,因为第一个模板的替换失败。这样做的原因是什么?我尚未在C ++标准中找到可以解释此问题的条目。 (这似乎与variadic模板有关,因为如果我修改了模板参数的顺序并将参数包放在首先,则代码将正确编译。)

The code is as follows.

#include <tuple>
#include <array>

template <typename T, typename Type>
struct Vec {
  using value_type = T;

  static constexpr size_t size() { return Type::size; }
};

template <size_t Size>
struct Const {
  static constexpr size_t size = Size;
};

template <class T, class Type, class = void>
struct vec_size_impl {};

template <class T, class Type>
struct vec_size_impl<T, Type, std::enable_if_t<std::is_arithmetic_v<T>>>
  : std::integral_constant<size_t, Type::size> {};

template <class T, class Type>
inline constexpr size_t Vec_size_v = vec_size_impl<T, Type>::value;

template<class T, class Type, size_t... Sizes>
// template<size_t... Sizes, class T, class Type>
  std::enable_if_t<
    ((0 + ... + Sizes) == Vec_size_v<T, Type>),
    std::tuple<Vec<T, Const<Sizes>>...>
  > split(const Vec<T, Type>&) noexcept {
      return std::make_tuple<Vec<T, Const<Sizes>>...>();
    }

template<class V, class Type>
  std::enable_if_t<
    (Vec_size_v<typename V::value_type, Type> % V::size() == 0),
    std::array<V, Vec_size_v<typename V::value_type, Type> / V::size()>
  > split(const Vec<typename V::value_type, Type>&) noexcept {
      return std::array<V, Vec_size_v<typename V::value_type, Type> / V::size()>();
    }


int main() {
  Vec<int, Const<6>> a;
  split<Vec<int, Const<2>>, Const<6>>(a);
  return 0;
}

Here (I think) the second split() can be matched, but I still got a compile error for the substitution fail of the first template. What is the reason for this? I have not yet found an entry in the C++ standard that can explain this problem. (This appears to be related to variadic templates, because if I modify the order of the template parameters and put the parameter pack in the first place, the code would compile correctly.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

爱你是孤单的心事 2025-01-29 02:47:28

仅当使用模板参数产生的无效类型或表达式出现在

  • 同一模板参数列表中的模板参数声明中,
  • SFINAE才适用于:使用默认模板参数(或者是Overload分辨率或类别的部分专业化匹配Selects) 函数模板的模板)
  • ,该函数的声明类型(包括其函数参数类型,返回类型或异常规范,但在从返回语句中推论占位符返回类型时,
  • 函数模板,其>显式( 常量表达 指定符,如果有
  • 类模板部分专业化,则在模板名称之后指定的模板参数

请参见 [temp.deduct]/8 - 这是“即时上下文”规则。

替换所有类型的别名和类型的别名模板基本上是在“在”模板参数替换之前发生的,因为 [temp.alias]/2 说使用别名模板始终等同于其替代。例如,这解释了为什么Sfinae适用于:: type std :: Enable_if_t在函数类型中 - 它等同于已注销的typedef std :: enable_if&lt; ... &gt; :: type,因此,当它形成无效类型时,它被认为是在函数模板的“直接上下文”中替代。类型的别名实际上根本不会像功能模板,类模板和变量模板一样“实例化”。

当Overload分辨率考虑第一个split模板时,它将尝试获得vec_size_v&lt; vec&lt; int,const&lt; 2&gt;&gt; 6这会导致该变量模板的专业化的隐式实例化。该变量模板的初始化程序的评估在该变量模板实例化范围内,而不在函数模板的函数类型中,因此Sfinae不应用,并且该变量模板具有错误,即使它在模板参数分辨率下扣除过程中发生。

显而易见的解决方法,虽然可能不是您想要的,但需要更长的vec_size&lt; t,type&gt; :: value而不是vec_size_v&lt; t,type&gt;

或者,您可以为vec_size_impl静态value成员提供主模板。但是它实际上并不需要具有数字类型:如果您执行

template <class T, class Type, class = void>
struct vec_size_impl
{
    struct none_t {};
    static constexpr none_t value;
};
// partial specialization as before

template <class T, class Type>
inline constexpr auto Vec_size = vec_size_impl<T, Type>::value;

第一个split的相同声明将获得其vec_size_v使用的实际有效常数表达式,但是无效的表达式(0 + ... + sizes)== vec_size_v&lt; t,type&gt;,因为没有匹配的operator ==。但是,这种无效的表达式在函数模板的函数类型中,因此Sfinae可以从过载分辨率过程中丢弃函数模板。

SFINAE applies only if the invalid type or expression resulting from a use of a template parameter appears within:

  • a template parameter declaration within the same template parameter list
  • a default template argument which is used (or will be if overload resolution or class partial specialization matching selects the template)
  • for a function template, the declared type of the function (including its function parameter types, return type, or exception specification, but not when deducing a placeholder return type from return statements),
  • for a function template, its explicit(constant-expression) specifier, if any
  • for a class template partial specialization, the template arguments specified after the template name

See [temp.deduct]/8 - this is the "immediate context" rule.

Substitution of all type aliases and type alias templates happens essentially "before" template argument substitution, since [temp.alias]/2 says a use of the alias template is always equivalent to its substitution. For example, this explains why SFINAE applies to the ::type member lookup in a std::enable_if_t within a function type - it is equivalent to the written-out typedef std::enable_if<...>::type, so when this forms an invalid type, it's considered to be in the "immediate context" of the function template argument substitution. Type aliases don't actually get "instantiated" at all like function templates, class templates, and variable templates do.

When overload resolution considers the first split template, it tries to get the value of Vec_size_v<Vec<int, Const<2>>, Const<6>>, which causes an implicit instantiation of that specialization of the variable template. The evaluation of that variable template's initializer is within that variable template instantiation, not within the function template's function type, so SFINAE does not apply and the variable template has an error, even though it happened during a template argument deduction for overload resolution.

The obvious workaround, though probably not what you want, is to require the longer Vec_size<T, Type>::value instead of Vec_size_v<T, Type>.

Or you could give the primary template for vec_size_impl a static value member. But it doesn't actually need to have a numeric type: if you do

template <class T, class Type, class = void>
struct vec_size_impl
{
    struct none_t {};
    static constexpr none_t value;
};
// partial specialization as before

template <class T, class Type>
inline constexpr auto Vec_size = vec_size_impl<T, Type>::value;

then the same declaration of the first split would get an actual valid constant expression for its Vec_size_v use, but an invalid expression (0 + ... + Sizes) == Vec_size_v<T, Type> since there's no matching operator==. But this invalid expression is within the function template's function type, so then SFINAE can discard the function template from the overload resolution process.

唱一曲作罢 2025-01-29 02:47:28

谢谢,问题现在解决了。错误消息如下:

cxx.cpp:24:62: error: no member named 'value' in 'vec_size_impl<Vec<int, Const<2>>, Const<6>>'
inline constexpr size_t Vec_size_v = vec_size_impl<T, Type>::value;
                                     ~~~~~~~~~~~~~~~~~~~~~~~~^
cxx.cpp:29:27: note: in instantiation of variable template specialization 'Vec_size_v<Vec<int, Const<2>>, Const<6>>' requested here
    ((0 + ... + Sizes) == Vec_size_v<T, Type>),
                          ^
cxx.cpp:46:3: note: while substituting explicitly-specified template arguments into function template 'split'
  split<Vec<int, Const<2>>, Const<6>>(a);
  ^

错误是因为替换故障是一个困难的错误,因为vec_size_impl :: value的扣除不在立即上下文中。可以通过vec_size_impl&lt; t替换vec_size_v&lt; t,type> vec_size_v&lt;

Thanks and the question is solved now. The error message is as follows:

cxx.cpp:24:62: error: no member named 'value' in 'vec_size_impl<Vec<int, Const<2>>, Const<6>>'
inline constexpr size_t Vec_size_v = vec_size_impl<T, Type>::value;
                                     ~~~~~~~~~~~~~~~~~~~~~~~~^
cxx.cpp:29:27: note: in instantiation of variable template specialization 'Vec_size_v<Vec<int, Const<2>>, Const<6>>' requested here
    ((0 + ... + Sizes) == Vec_size_v<T, Type>),
                          ^
cxx.cpp:46:3: note: while substituting explicitly-specified template arguments into function template 'split'
  split<Vec<int, Const<2>>, Const<6>>(a);
  ^

and the error is because the substitution failure is a hard error, since the deduction of Vec_size_impl::value is not in the immediate context. The question can be solved by replacing the Vec_size_v<T, Type> by Vec_size_impl<T, Type>::value in the enable_if.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文