为什么我们可以使用SFINAE检测operator()的默认参数值,但不检测自由函数和PMF的默认参数值? [英] Why can we detect the presence of default parameter values of operator() with SFINAE, but not those of free functions and PMFs?

查看:149
本文介绍了为什么我们可以使用SFINAE检测operator()的默认参数值,但不检测自由函数和PMF的默认参数值?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的程序中,情况1尝试通过指针到成员函数使用默认参数。情况2尝试通过函数引用使用默认参数。情况3使用 operator()中的默认参数。这里唯一有趣的断言是那些使用别名 can_call_with_one - 其他存在来证明设置的正确性。



在我可用的最新版本的GCC,Clang和MSVC中,这个程序失败了case 1和2中的单参数断言。



我的问题是两个:


  1. 这些结果与ISO C ++标准是否一致?

  2. 情况3不会失败?



  #include< type_traits> 
#include< utility>

struct substitution_failure {};

substitution_failure check(...);

template< typename Pmf,typename T,typename ... Args>
自动检查(Pmf pmf,T t,Args& ... args) - >
decltype((t。* pmf)(std :: forward< Args>(args)...))*;

template< typename Fn,typename ... Args>
自动检查(Fn& f,Args& ... args) - >
decltype(f(std :: forward< Args>(args)...))*;

template< typename T>
using test_result = std :: integral_constant< bool,
!std :: is_same< T,substitution_failure> :: value
>

template< typename ... ts>
auto can_invoke(Ts&& ... ts) - >
test_result< decltype(check(std :: forward< Ts>(ts)...))> ;;

命名空间case_1 {

//指向成员函数的指针

struct foo {
int bar(int,int = 0);
};

using can_call_with_one = decltype(can_invoke(& foo :: bar,foo {},0));
using can_call_with_two = decltype(can_invoke(& foo :: bar,foo {},0,0));
using can_call_with_three = decltype(can_invoke(& foo :: bar,foo {},0,0,0));

static_assert(can_call_with_one {},case 1 - 无法使用一个参数调用);
static_assert(can_call_with_two {},case 1 - 无法使用twp参数调用);
static_assert(!can_call_with_three {},case 1 - 可以调用三个参数);
}

命名空间case_2 {

//函数引用

int foo(int,int = 0);

使用can_call_with_one = decltype(can_invoke(foo,0));
using can_call_with_two = decltype(can_invoke(foo,0,0));
using can_call_with_three = decltype(can_invoke(foo,0,0,0));

static_assert(can_call_with_one {},case 2 - 无法使用一个参数调用);
static_assert(can_call_with_two {},case 2 - 不能调用两个参数);
static_assert(!can_call_with_three {},case 2 - can call with three arguments);
}


命名空间case_3 {

//函数对象

struct foo {
int operator )(int,int = 0);
};

使用can_call_with_one = decltype(can_invoke(foo {},0));
using can_call_with_two = decltype(can_invoke(foo {},0,0));
using can_call_with_three = decltype(can_invoke(foo {},0,0,0));

static_assert(can_call_with_one {},case 3 - 无法使用一个参数调用);
static_assert(can_call_with_two {},case 3 - 不能调用两个参数);
static_assert(!can_call_with_three {},case 3 - 可以调用三个参数);
}

int main(){return 0;
=nofollow> runnable version

解决方案

函数的类型trait不附带默认参数。如果它将不能够分配一个具有默认值的函数的指针,如:

  void foo int = 0){...} 

到:

  void(* fp)(int,int); 
fp =& foo;

现在的问题是,如果语言允许的话 - 参数还标识一个函数的类型?这意味着参数的默认值应该是 constexpr ,因此会限制默认值的可用性。这种方式,例如 const char * 的参数不能具有内定的默认值...



On另一方面,如果函数的类型仅携带给定参数具有默认值而不知道值本身的信息 - 编译器将不能够从指针重建该函数的默认值,而函数调用。


In the program below, case 1 attempts to use a default parameter via pointer-to-member-function. Case 2 attempts to use a default parameter via function reference. Case 3 uses the default parameter in operator(). The only interesting assertions here are the ones using the alias can_call_with_one - the others exist to prove correctness of the setup.

In the latest versions of GCC, Clang, and MSVC that are available to me, this program fails the single-argument assertions in cases 1 and 2.

My question is twofold:

  1. Are these results consistent with the ISO C++ standard?
  2. If so, why does case 3 not fail?

#include <type_traits>
#include <utility>

struct substitution_failure {};

substitution_failure check(...);

template<typename Pmf, typename T, typename... Args>
auto check(Pmf pmf, T t, Args&&... args) ->
    decltype((t.*pmf)(std::forward<Args>(args)...))*;

template<typename Fn, typename... Args>
auto check(Fn&& f, Args&&... args) ->
    decltype(f(std::forward<Args>(args)...))*;

template<typename T>
using test_result = std::integral_constant<bool,
    !std::is_same<T, substitution_failure>::value
>;

template<typename... Ts>
auto can_invoke(Ts&&... ts) ->
    test_result<decltype(check(std::forward<Ts>(ts)...))>;

namespace case_1 {

    //pointer to member function

    struct foo {
        int bar(int, int = 0);
    };

    using can_call_with_one = decltype(can_invoke(&foo::bar, foo{}, 0));
    using can_call_with_two = decltype(can_invoke(&foo::bar, foo{}, 0, 0));
    using can_call_with_three = decltype(can_invoke(&foo::bar, foo{}, 0, 0, 0));

    static_assert(can_call_with_one{}, "case 1 - can't call with one argument");
    static_assert(can_call_with_two{}, "case 1 - can't call with twp arguments");
    static_assert(!can_call_with_three{}, "case 1 - can call with three arguments");
}

namespace case_2 {

    //function reference

    int foo(int, int = 0);

    using can_call_with_one = decltype(can_invoke(foo, 0));
    using can_call_with_two = decltype(can_invoke(foo, 0, 0));
    using can_call_with_three = decltype(can_invoke(foo, 0, 0, 0));

    static_assert(can_call_with_one{}, "case 2 - can't call with one argument");
    static_assert(can_call_with_two{}, "case 2 - can't call with two arguments");
    static_assert(!can_call_with_three{}, "case 2 - can call with three arguments");
}


namespace case_3 {

    //function object

    struct foo {
        int operator()(int, int = 0);
    };

    using can_call_with_one = decltype(can_invoke(foo{}, 0));
    using can_call_with_two = decltype(can_invoke(foo{}, 0, 0));
    using can_call_with_three = decltype(can_invoke(foo{}, 0, 0, 0));

    static_assert(can_call_with_one{}, "case 3 - can't call with one argument");
    static_assert(can_call_with_two{}, "case 3 - can't call with two arguments");
    static_assert(!can_call_with_three{}, "case 3 - can call with three arguments");
}

int main() { return 0; }

runnable version

解决方案

Type trait of the function does not come with the information of the default parameters. If it would you wouldn't be able to assign a pointer of a function with default value like:

void foo(int, int = 0) {...}

to:

void(*fp)(int, int);
fp = &foo;

The question is now if the language would allow for that - should the values that are default for a given parameters also identify the type of a function? This would imply that the default value of the parameter should be a constexpr, and as such would restrict the usability of the default values. This way for example parameters of type const char * couldn't have default value defined inline...

On the other hand if the type of the function would carry only the information that a given parameter has a default value without the knowledge of the value itself - compiler wouldn't be able to reconstruct the default value of the function from the pointer while the function invocation.

这篇关于为什么我们可以使用SFINAE检测operator()的默认参数值,但不检测自由函数和PMF的默认参数值?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆