【问题标题】:Why Can't These Templatized Functions Take No Arguments?为什么这些模板化函数不能不带参数?
【发布时间】:2016-05-06 14:46:48
【问题描述】:

我正在尝试为Substitution Fail Is Not An Error(SFINAE) 使用几个模板化函数。我可以这样做:

template<typename R, typename S = decltype(declval<R>().test())> static true_type Test(R*);
template<typename R> static false_type Test(...);

但我不明白这个论点是如何使这个 SNFIAE 工作的。似乎我应该能够删除参数,并且模板选择的工作方式完全相同:

template<typename R, typename S = decltype(declval<R>().test())> static true_type Test();
template<typename R> static false_type Test();

但它没有,我明白了:

重载 'Test()' 的调用不明确

这些论点使 SFINAE 发挥作用的原因是什么?

【问题讨论】:

    标签: c++ function parameters arguments sfinae


    【解决方案1】:

    您的第二个示例无法编译,因为 Test 有两个具有相同签名的重载,因为默认模板类型参数不是函数签名的一部分。这是不允许的。

    您的第一个示例以下列方式工作:

    当类型 R 中确实有函数 test 时,Test 都成为有效的重载候选者。但是,省略号函数的排名低于非省略号函数,因此编译器选择第一个重载,返回 true_type

    当 R 上没有 test 时,第一个重载将被排除在重载决议集之外(SFINAE 正在工作)。你只剩下第二个,它返回false_type

    【讨论】:

    • 但除此之外,这些参数类型有一些特别之处,可以使它们以正确的顺序进行评估。我不明白为什么编译器更喜欢R* 而不是...。你能解释一下吗?
    • @JonathanMee,将添加到答案中。
    • @JonathanMee 简单地说, (...) 是执行重载解析时与任何参数列表最不受欢迎的匹配项。这是设计使然,并且是标准中规定的。巧合的是,这也是实现具有可变参数的函数的最不受欢迎的方式:-)
    • JonathanMee,暂时离开 SFINAE,并尝试先了解重载解决过程。当您有两个函数签名时,一个具有默认参数,另一个具有省略号(或没有参数!)所有这些重载具有相同的等级。 @RichardHodges 说省略号总是排名最低时并不完全正确。
    【解决方案2】:

    问题已得到解答,但深入解释可能会有所帮助。

    希望这个带注释的程序能让事情更清楚:

    #include <utility>
    #include <iostream>
    
    // define the template function Test<R> if and only if the expression
    // std::declval<R>().test()
    // is a valid expression.
    // in which case Test<R, decltype(std::declval<R>().test())>(0) will be preferrable to... (see after)
    template<typename R, typename S = decltype(std::declval<R>().test())> 
      static std::true_type Test(R*);
    
    // ...the template function Test<R>(...)
    // because any function overload with specific arguments is preferred to this
    template<typename R> static std::false_type Test(...);
    
    
    struct foo
    {
      void test();
    };
    
    struct bar
    {
      // no test() method
    //  void test();
    };
    
    
    // has_test<T> deduces the type that would have been returned from Test<T ... with possibly defaulted args here>(0)
    // The actual Test<T>(0) will be the best candidate available
    // For foo, it's Test<foo, decltype(std::declval<R>().test())>(foo*)
    // which deduces to
    // Test<foo, void>(foo*)
    // because that's a better match than Test<foo>(...)
    //
    // for bar it's Test<bar>(...)
    // because Test<bar, /*error deducing type*/>(bar*)
    // is discarded as a candidate, due to SFNAE
    //
    template<class T>
    constexpr bool has_test = decltype(Test<T>(0))::value;
    
    
    int main()
    {
      std::cout << has_test<foo> << std::endl;
      std::cout << has_test<bar> << std::endl;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-16
      • 2013-01-17
      • 2021-09-25
      • 1970-01-01
      • 2011-07-03
      • 1970-01-01
      相关资源
      最近更新 更多