【问题标题】:SFINAE without void_t (maybe a template specialization question)没有 void_t 的 SFINAE(可能是模板专业化问题)
【发布时间】:2021-06-05 11:02:29
【问题描述】:

对不起标题,我不确定我的问题的类别。 我正在尝试用 SFINAE 做一个 is_incrementable。这很好用,但是当我尝试更深入地理解它时,当我删除 void_t 时,代码 sn-p 无法按预期工作。

原代码:

#include <iostream>

template< typename, typename = void >
struct is_incrementable : std::false_type { };

template< typename T >
struct is_incrementable<T,
          std::void_t<decltype( ++std::declval<T&>() )>

       > : std::true_type { };

int main()
{
    std::cout << is_incrementable<int>::value << std::endl;  // prints 1
    std::cout << is_incrementable<std::string>::value << std::endl;  // prints 0
    return 0;
}

i) is_incrementable&lt;int&gt;::value 的计算结果为 is_incrementable&lt;int, void&gt;::value,它也是原始模板类和特化。在这种情况下,编译器选择了专门的版本,所以值等于 1。 对于字符串版本,特化失败,SFINAE 启动,所以我们只有基本模板。 (值等于0)

ii) 当我更改代码并删除 void_t

template< typename, typename = void > 
struct is_incrementable : std::false_type { };

template< typename T >
struct is_incrementable<T,
        decltype( ++std::declval<T&>() )    // void_t is removed              
       > : std::true_type { };

int main()
{
    std::cout << is_incrementable<int>::value << std::endl;  // prints 0
    std::cout << is_incrementable<std::string>::value << std::endl;  // prints 0
    return 0;
}

0 和 0 被打印。 is_incrementable&lt;int&gt;::value 表示is_incrementable&lt;int, void&gt;::value,专业化是 is_incrementable&lt;int, int&gt;::value(我认为),所以我们使用基本模板。对于字符串,特化无论如何都会失败。

我的问题: iii) 有趣的部分。 如果现在我将第一行更改为使用 int 作为默认类型

#include <iostream>

template< typename, typename = int >  // !!! int is used now
struct is_incrementable : std::false_type { };

template< typename T >
struct is_incrementable<T,
        decltype( ++std::declval<T&>() )  // void_t is removed 
       > : std::true_type { };

int main()
{
    std::cout << is_incrementable<int>::value << std::endl;  // prints 0
    std::cout << is_incrementable<std::string>::value << std::endl;  // prints 0
    return 0;
}

然后再次打印 0 和 0。 为什么? is_incrementable&lt;int&gt;::value 表示(我认为)is_incrementable&lt;int, int&gt;::valuedecltype( ++std::declval&lt;T&amp;&gt;() ) 也应该是 int 。 所以我认为编译器应该使用专门的版本。 (并且应该打印 1)

如果我删除 decltype( ++std::declval&lt;T&amp;&gt;() ) 并写入int,然后打印 1 和 1(这是预期的打印输出)。

请有人解释一下 iii) 中发生了什么。

【问题讨论】:

    标签: c++ sfinae void-t


    【解决方案1】:

    Tint 时,decltype( ++std::declval&lt;T&amp;&gt;() )int &amp;,而不是int。所以要得到你期望的输出,你可以改变这个:

    template< typename, typename = int >
    struct is_incrementable : std::false_type { };
    

    到这里:

    template< typename, typename = int & >
    struct is_incrementable : std::false_type { };
    

    或者改变这个:

    template< typename T >
    struct is_incrementable<T,
            decltype( ++std::declval<T&>() )
           > : std::true_type { };
    

    到这里:

    template< typename T >
    struct is_incrementable<T,
            std::remove_reference_t<
              decltype( ++std::declval<T&>() )
            >
           > : std::true_type { };
    

    【讨论】:

    • 感谢您的回答 ruakh!你是对的,你提到的两种解决方案都有效。还有一件事,如果我将 decltype 行更改为“decltype(++std::declval())”(所以我删除了 &),那么无论第一行如何,它都会再次打印 0 和 0 (" typename = int" 或 "typename = int&")
    • @thamas, ++std::declval&lt;T&gt;() 来自is_incrementable&lt;int&gt;::value 时格式错误,即来自T == int。您可以通过尝试编译 ++std::declval&lt;int&amp;&gt;();++std::declval&lt;int&gt;(); 来验证这一点。
    • @thamas:解释 Enlico 的评论:++ 需要一个左值;也就是说,++3 之类的内容无效。
    • 好的,所以 declval 使用 add_rvalue_reference,所以使用 将返回 int&&,并且不能递增。非常感谢你们俩!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-21
    相关资源
    最近更新 更多