简体   繁体   English

如何在构造函数上正确使用std :: enable_if

[英]How to properly use std::enable_if on a constructor

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. 我正在尝试使用std::enable_if来作为lambda表达式用作输入时由于歧义函数签名而有条件地调用正确的构造函数,但是所述lambda表达式的参数可以隐式地相互转换。

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. 这是在以下问题的基础上进行的尝试: Here ,但有足够的区别,并且重点关注std::enable_if以提出另一个问题。 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 . 然后,我尝试运行下面的代码,但是, std::enable_if部分似乎不起作用,但是我知道方括号内的所有内容都可以(或应该)如现场示例所示进行工作

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 . 我正在这里处理示例5。

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. 有关依赖名称以及何时需要templatetypename名称的更多信息,请参见这篇文章。

enable_if

typename = std::enable_if_t<condition>

should instead be 相反应该是

std::enable_if_t<condition>* = nullptr

as @Jarod42 mentioned. 正如@ Jarod42所提到的。 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 function_traits不能与重载或模板化的operator() ,而是可以替换掉

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. 检查可调用对象是否可以使用不带转换的确切参数进行调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM