如何在构造函数上正确使用std :: enable_if [英] How to properly use std::enable_if on a constructor

查看:146
本文介绍了如何在构造函数上正确使用std :: enable_if的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题结合了几段代码,有点复杂,但是我尝试将其尽可能缩小.

由于使用lambda表达式作为输入时,由于函数签名不明确,我正在尝试使用 std :: enable_if 有条件地调用正确的构造函数,但可以将所述lambda表达式的参数设置为可以彼此隐式转换.

这是基于以下问题的尝试:实时示例,用于注释掉的问题部分.

要检查函子的参数(和结果)类型,我有以下类:

 模板< typename T>struct function_traits:public function_traits< decltype(& T :: operator())>{};//对于泛型类型,直接使用其'operator()'签名的结果模板< typename ClassType,typename ReturnType,typename ... Args>struct function_traits< ReturnType(ClassType :: *)(Args ...)const>//我们专门针对成员函数的指针{枚举{num_args = sizeof ...(Args)};typedef ReturnType result_type;模板< size_t N>结构参数{typedef typename std :: tuple_element< N,std :: tuple< Args ...>> :: type type;//第i个参数等效于元组的第i个tuple元素//由这些参数组成.};}; 

然后我尝试运行以下代码,但是 std :: enable_if 部分似乎不起作用,但是我知道方括号内的所有内容都可以(或应该)正常工作,如下所示实时示例.

  template< typename数据类型,typename类型1,typename类型2>A级{上市:使用a_type = std :: tuple< Type1,Type2> ;;使用b_type = std :: tuple< std :: size_t,std :: size_t> ;;模板< typename Lambda,typename = std :: enable_if_t< std :: is_same< typename function_traits< Lambda> :: arg< 0> :: type,b_type> ::: value>>A(const Lambda& Initializer){std :: cout<<"idx_type"<<std :: endl;}模板< typename Lambda,typename = std :: enable_if_t< std :: is_same< typename function_traits< Lambda> :: arg< 0> :: type,a_type> ::: value>>A(const Lambda& Initializer){std :: cout<<"point_type"<<std :: endl;}};int main(){自动f = [](std :: tuple< long long,int>)->double {return 2;};std :: cout<<std :: is_same<类型名function_traits< decltype(f)> :: arg< 0> :: type,std :: tuple< std :: size_t,std :: size_t>> ::: value<<std :: is_same<类型名function_traits< decltype(f)> :: arg< 0> :: type,std :: tuple< long long,int>> :: value;自动a = A  {[](std :: tuple< long long,int>)->double {return 1;}};自动b = A< double,long long,int> {[](std :: tuple< std :: size_t,std :: size_t>)->double {return 2;}};} 

那我想念什么?我正在示例5 #a 此处.

解决方案

从属名称

  typename function_traits< Lambda> :: template arg< 0> ::: type^^^^^^^^^ 

请参见帖子以获取有关相关名称以及何时需要 template typename 的更多信息.

enable_if

  typename = std :: enable_if_t< condition> 

应该是

  std :: enable_if_t< condition> * = nullptr 

就像@ Jarod42提到的那样.这是因为否则构造函数将是相同的,并且无法重载.它们的默认值不同不会改变这一事实.有关更多信息,请参见.

将它们放在一起

  template< typename Lambda,std :: enable_if_t< std :: is_same_v< typename function_traits< Lambda> :: template arg< 0> :: type,a_type>> * = nullptr>A(const Lambda&); 

实时

旁注

function_traits 不适用于过载的或模板化的 operator(),而是可以替换的

  template< typename T,typename ... Args>使用return_type = decltype(std :: declval< T>()(std :: declval< Args>()...));template< typename T,typename ... Args>使用mfp = decltype(static_cast< return_type< T,Args ...>(T :: *)(Args ...)const>(& T :: operator()));template< typename Lambda,mfp< Lambda,a_type>= nullptr>A(const Lambda&); 

检查可调用对象是否可以使用不带转换的确切参数调用.

This question combines several pieces of code and is a bit complicated, but I tried slimming it down as much as possible.

I am trying to use std::enable_if to conditionally invoke the correct constructor as a result of ambiguous function signatures when a lambda expression is used as input, but the parameters of said lambda expression can be implicitly convertible to one another.

This is an attempt to build upon the following question: Here, but is sufficiently different and focuses on std::enable_if to merit another question. I am also providing the Live Example that works with the problem parts commented out.

To inspect the argument (and result) types of the functor, I have the following class:

template <typename T>
struct function_traits
    : public function_traits<decltype(&T::operator())>
{};
// For generic types, directly use the result of the signature of its 'operator()'

template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
    // we specialize for pointers to member function
{
    enum { num_args = sizeof...(Args) };

    typedef ReturnType result_type;

    template <size_t N>
    struct arg
    {
        typedef typename std::tuple_element<N, std::tuple<Args...>>::type type;
        // the i-th argument is equivalent to the i-th tuple element of a tuple
        // composed of those arguments.
    };
};

Then I try to run the below code, however, the std::enable_if part does not seem to work, but I know that everything within the brackets does (or should) work as demonstrated by the Live Example.

template<typename data_type, typename Type1, typename Type2>
class A
{
public:
    using a_type = std::tuple<Type1, Type2>;
    using b_type = std::tuple<std::size_t,std::size_t>;

    template<typename Lambda, typename = std::enable_if_t<std::is_same<typename function_traits<Lambda>::arg<0>::type, b_type>::value>>
    A(const Lambda& Initializer)
    {
        std::cout << "idx_type" << std::endl;
    }
    template<typename Lambda, typename = std::enable_if_t<std::is_same<typename function_traits<Lambda>::arg<0>::type, a_type>::value>>
    A(const Lambda& Initializer)
    {
        std::cout << "point_type" << std::endl;
    }
};

int main()
{
    auto f = [](std::tuple<long long, int>) -> double { return 2; };

    std::cout << std::is_same<typename function_traits<decltype(f)>::arg<0>::type, std::tuple<std::size_t, std::size_t>>::value
        << std::is_same<typename function_traits<decltype(f)>::arg<0>::type, std::tuple<long long, int>>::value;

    auto a = A<double, long long, int>{
        [](std::tuple<long long, int>) -> double { return 1; }
    };

    auto b = A<double, long long, int>{
        [](std::tuple<std::size_t, std::size_t>) -> double { return 2; }  
    };

}

So what am I missing? I am working off example #5 here.

解决方案

Dependent names

typename function_traits<Lambda>::template arg<0>::type
                                  ^^^^^^^^

See this post for more information on dependent names and when template or typename is needed.

enable_if

typename = std::enable_if_t<condition>

should instead be

std::enable_if_t<condition>* = nullptr

as @Jarod42 mentioned. This is because the constructors would otherwise be identical and unable to be overloaded. That their default values differ doesn't change this fact. See this for more information.

Putting it together is

template<typename Lambda, std::enable_if_t<std::is_same_v<typename function_traits<Lambda>::template arg<0>::type, a_type>>* = nullptr>
A(const Lambda&);

Live

Side note

function_traits won't work with either overloaded or templated operator(), it can instead be replaced

template<typename T, typename... Args>
using return_type = decltype(std::declval<T>()(std::declval<Args>()...));

template<typename T, typename... Args>
using mfp = decltype(static_cast<return_type<T, Args...>(T::*)(Args...) const>(&T::operator()));

template<typename Lambda, mfp<Lambda, a_type> = nullptr>
A(const Lambda&);

To check if the callable can be called with the exact arguments without conversions.

这篇关于如何在构造函数上正确使用std :: enable_if的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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