定义一个结构,只要获得给定的SFINAE-able构造函数,该结构就是从true_type派生的

时间:2016-03-06 14:05:42

标签: c++ templates c++14 sfinae typetraits

请考虑以下代码段:

template<typename T, class Tuple>
class vector
{
    using size_type = typename Tuple::size_type;

    template<typename... Elements,
        typename = decltype(std::declval<Tuple>().reserve(size_type()))>
        typename = decltype(std::declval<Tuple>().push_back(T())),
    vector(Elements&&... elements)
    { /* ... */ }
};

我想定义一个嵌套的结构supports_reserve_push_back,它是从std::true_type派生的,只要上面的构造函数被启用(在另一种情况下派生自std::false_type)。

我该怎么做?

4 个答案:

答案 0 :(得分:2)

我修改了代码以使其成为build。并根据我的理解实施了您所要求的特质。

#include <iostream>
#include <type_traits>
#include <vector>
#include <map> 

namespace example {

template<typename...>
using void_t = void;

template<typename T, class Tuple>
struct vector {
    using size_type = typename Tuple::size_type;
    using tuple_type = Tuple;
    using elem_type = T;

    template<typename... Elements>
    vector(Elements&&... elements)
    { /* ... */ }
};

template <class T, typename = void>
struct supports_reserve_push_back : std::false_type {};

template <class Vec> 
struct supports_reserve_push_back<Vec, void_t<
  decltype(std::declval<typename Vec::tuple_type>().reserve(typename Vec::size_type())),
  decltype(std::declval<typename Vec::tuple_type>().push_back(typename Vec::elem_type())) >
>
  : std::true_type {};

}

int main() {
    std::cout
    << example::supports_reserve_push_back<example::vector<int, std::vector<int>>>::value
    << '\n'
    << example::supports_reserve_push_back<example::vector<int, std::map<int, int>>>::value;
    return 0;
}

有一点需要注意:

  1. 在负面情况下实例化类时,编写c'tor的方式最初会导致硬错误。这就是我从c'tor中删除chcck的原因。

  2. 我建议你首先定义类型特征,并使用它们来启用你的c'tors。

答案 1 :(得分:1)

#include <type_traits>
#include <utility>

template <typename...>
using void_t = void;

template <typename AlwaysVoid, template <typename...> class Operation, typename... Args>
struct detect_impl : std::false_type {};

template <template <typename...> class Operation, typename... Args>
struct detect_impl<void_t<Operation<Args...>>, Operation, Args...> : std::true_type {};

template <template <typename...> class Operation, typename... Args>
using detect = detect_impl<void_t<>, Operation, Args...>;

template <typename T, typename Sz>
using has_reserve = decltype(std::declval<T>().reserve(std::declval<Sz>()));

template <typename T, typename U>
using has_push_back = decltype(std::declval<T>().push_back(std::declval<U>()));

template <typename Tuple, typename T, typename size_type>
constexpr bool supports_reserve_push_back = detect<has_reserve, Tuple, size_type>{} && detect<has_push_back, Tuple, T>{};

测试:

template <typename T, class Tuple>
class vector
{
public:
    using size_type = typename Tuple::size_type;

    template <typename... Elements, typename U = Tuple,
    std::enable_if_t<supports_reserve_push_back<U&, T, size_type>, int> = 0>
    vector(Elements&&... elements)
    {
    }

    template <typename... Elements, typename U = Tuple,
    std::enable_if_t<!supports_reserve_push_back<U&, T, size_type>, int> = 0>
    vector(Elements&&... elements)
    {
    }
};

DEMO

答案 2 :(得分:1)

namespace details{
  template<template<class...>class Z,class,class...Ts>
  struct can_apply:std::false_type{};
  template<template<class...>class Z,class...Ts>
  struct can_apply<Z,std::void_t<Z<Ts...>,Ts...>:
  std::true_type{};
}
template<template<class...>class Z,class... Ts>
using can_apply=details::can_apply<Z,void,Ts...>;

将你的decltypes包装成模板使用并做一些&&并完成。

还有一个类似于上面的std实验。

template<class T, class U>
using push_back_r = decltype(std::declval<T>().push_back(std::declval<U>()));
template<class T>
using reserve_r = decltype(std::declval<T>().reserve(1));

template<class T, class U>
constexpr can_apply<push_back_r,T,U> has_push_back={};
template<class T>
constexpr can_apply<reserve_r,T> has_reserve={};

template<bool b>using bool_t=std::integral_constant<bool,b>;

template<class T,class U>
constexpr bool_t<has_push_back<T,U>&&has_reserve<T>>
efficiently_fillable_with = {};

然后efficiently_fillable_with<T,U>是真实类型,如果您可以使用T保留空间然后将我们推入其中。保留TU的r / l值类别:如果您想了解使用右值T s填充U的非同步左值:

efficiently_fillable_with<T&,U>

如果您想填写U const&而不是rvalues,请传递U const&

答案 3 :(得分:0)

我会尝试以下方法:

让您的vector模板类继承自超类,如下所示:

template<typename T, class Tuple> class vector
   : public supports_reserve_push_back_impl<
       vector_has_default_constructor<T, Tuple>::value() > {

// ...

}

现在,定义一个采用相同模板参数的vector_has_default_constructor模板类:

template<typename T, class Tuple> class vector_has_default_constructor {

public:
// ...

};

vector_has_default_constructor

  • 使用与向量构造函数相同的完全签名定义constexpr bool value()方法。此constexpr方法返回true。

  • 使用constexpr bool value()签名定义重载...,在重载决策中应该具有较低的优先级。此constexpr返回false。

现在,这种情况被简化为定义两个简单的特化,supports_reserve_push_back_impl<true>supports_reserve_push_back_impl<false>

supports_reserve_push_back_impl<true>包含您所需的supports_reserve_push_back值,并由vector继承。

supports_reserve_push_back_impl<false>为空。