【问题标题】:How to properly use std::enable_if on a constructor如何在构造函数上正确使用 std::enable_if
【发布时间】:2018-08-23 12:40:52
【问题描述】:

这道题结合了几段代码,有点复杂,不过我尽量精简了。

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

这是在以下问题的基础上进行的尝试:Here,但与std::enable_if 有很大不同,因此值得提出另一个问题。我还提供了Live Example,它适用于注释掉的问题部分。

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

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.
    };
};

然后我尝试运行下面的代码,但是,std::enable_if 部分似乎不起作用,但我知道括号内的所有内容都可以(或应该)按照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; }  
    };

}

那么我错过了什么?我正在处理示例#5 here

【问题讨论】:

  • 等一下,您的意思是向std 命名空间添加一个类吗?除了极少数例外,这样做是明确的未定义行为
  • 是的,有人告诉我不要这样做,但这不是造成或破坏这个特定问题的原因。只是一些我忘记清理的遗留物。
  • typename = std::enable_if_t&lt;cond&gt; 应该是 std::enable_if_t&lt;cond, bool&gt; = false
  • std::enable_if 旨在用于在编译时初始化默认构造函数或函数参数。因此,根据编译时表达式评估的结果,将从可能的同名构造函数/函数列表中选择或不选择构造函数或函数。看看 std::shared_ptr 类,它使用了这种技术。
  • 我从function_traits 中删除了有问题的std,并且我尝试了std::enable_if_t&lt;cond, bool&gt; = false,但它不起作用。 @Jarod42,你是什么意思?

标签: c++ template-meta-programming enable-if


【解决方案1】:

从属名称

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

有关依赖名称以及何时需要 templatetypename 的更多信息,请参阅 this 帖子。

enable_if

typename = std::enable_if_t<condition>

应该是

std::enable_if_t<condition>* = nullptr

正如@Jarod42 提到的。这是因为构造函数否则将是相同的并且无法重载。它们的默认值不同并不会改变这一事实。请参阅this 了解更多信息。

把它放在一起是

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

旁注

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&);

检查是否可以在不进行转换的情况下使用确切的参数调用可调用对象。

【讨论】:

  • 感谢您的详细解释,这看起来很棒!一旦我再次醒来,我相信我会学到 10 件新东西。
  • 所以这确实对我有用,但是,VC++ 在如何使用派生类中的类型方面仍然非常挑剔。在我最初的用法中,a_typeb_type 实际上是派生的,并进一步由其他类型组成。在 gcc 中,我可以只做 Parent::a_type 并完成,在 VC++ 中,我需要自己覆盖父类的类型,并且在定义子类的类型时不能使用父类中的任何内容。幸运的是,这不是设计破坏问题,但有点烦人。感谢您的代码。
猜你喜欢
  • 2022-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-28
  • 1970-01-01
  • 2013-07-24
  • 1970-01-01
相关资源
最近更新 更多