【问题标题】:Why does SFINAE not work in such a case?为什么 SFINAE 在这种情况下不起作用?
【发布时间】:2017-03-25 14:45:19
【问题描述】:
#include <iostream>
#include <type_traits>

template<typename T>
struct A
{
    using m = std::remove_pointer_t<T>&;
};

template
<
    typename T,
    typename = std::void_t<>
>
struct Test
{
    enum { value = 0 };
};

template<typename T>
struct Test<T, typename A<T>::m>
{
    enum { value = 1 };
};

int main()
{
    std::cout << Test<void*&>::value; // ok, output 0
    std::cout << Test<void*>::value; // error : cannot form a reference to 'void'
}

第一种情况输出0,表示选择了主模板。所以,我认为第二种情况也应该选择主模板而不是专用模板;那么,应该不会有错误。

预计Test&lt;void*&amp;&gt;可以;让我吃惊的是Test&lt;void*&gt;应该不行!

为什么 SFINAE 在后一种情况下不起作用?

【问题讨论】:

  • 可能是这种情况,因为A&lt;T&gt;::m 是不可演绎的上下文。
  • 如果是,为什么前一种情况可以?
  • 因为参考崩溃和remove_pointer 无效。
  • 没有选择专用模板并不意味着它没有被实例化。您的第二种情况(如果有效)也将选择主模板。只需将void 替换为int 即可看到它会打印出00
  • g++ 6.3.0 (-std=c++17) and clang 3.8.0 (-std=c++1z) and Visual Studio 2015 all print 0 for Test&lt;int*&gt;::value.

标签: c++ overloading sfinae c++17 typetraits


【解决方案1】:

您的第二种情况是一个硬错误。

SFINAE @ cppreference.com 说:

只有函数类型或其模板参数类型的直接上下文中的类型和表达式的失败才是 SFINAE 错误。如果替换类型/表达式的评估导致副作用,例如某些模板特化的实例化、隐式定义的成员函数的生成等,这些副作用中的错误将被视为硬错误。

第一种情况是可以的,因为如果Tvoid*&amp;remove_pointer 无效,那么mvoid*&amp;,因为引用折叠并且对指向 void 的指针的引用是有效类型,而引用无效不是。

第一种情况的类型仍然是Test&lt;void*&amp;, void&gt; 而不是Test&lt;void*&amp;, void*&amp;&gt;,因为您只指定了第一个模板参数。

第二种情况失败但第一种情况失败的原因是编译器必须实例化专用模板,因为第二个参数是非推导上下文,因此编译器无法立即判断专用化是否会更好匹配。但在第二种情况下,实例化会产生硬错误,而在第一种情况下则不会。

仍然选择主模板,因为它是一个更好的匹配(而专用模板仍将被实例化以检查它是否匹配)。

注意:我不能说特化是否真的被完全实例化,或者编译器是否只是在查找typename A&lt;T&gt;::m 以检查这种特化是否会更好地匹配。然而结果是一样的。如果是void*,则会出现硬错误。

还要注意,在使用 C++17 时,可能更愿意使用 constexpr 成员而不是枚举。

template<typename T>
struct Test<T, typename A<T>::m>
{
    static constexpr unsigned value = 1u;
};

【讨论】:

  • 第一种情况输出0,表示重载的类是主模板,而不是专门的模板。
  • 如果你的说法是真的,那么第一种情况会输出1,因为它使用了专门的模板。
  • 如果第一种情况输出0,那么第二种情况也应该输出0。这正是我想知道的。
  • @xmllmx:第一条语句打印0是完全有效的。要打印1,您必须说Test&lt;void*&amp;, void*&amp;&gt;
猜你喜欢
  • 2017-11-26
  • 2010-12-29
  • 2021-08-05
  • 2011-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多