每当成员变量可以由可变参数构造时,有条件地启用构造函数

时间:2016-04-25 15:50:48

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

我有一个带有模板参数foo的类Tuple,我想提供一个可变参数构造函数来初始化类型m_elements的成员变量Tuple,每当表达式m_elements{ static_cast<typename Tuple::value_type>(std::forward<Elements>(elements))... }已定义。

我们可以通过以下方式执行此操作:

template<class Tuple>
struct foo
{
    using value_type = typename Tuple::value_type;

    template<class... Elements, class U = Tuple,
        class = decltype(U{ static_cast<value_type>(std::declval<Elements>())... })>
    foo(Elements&&... elements)
        : m_elements{ static_cast<value_type>(std::forward<Elements>(elements))... }
    {}

    Tuple m_elements;
};

现在,是否启用此构造函数还应该依赖于其他一些条件。所以,我需要写一些像

这样的东西
template<class... Elements, class U = Tuple,
    class = std::enable_if_t</* some other conditions depending on U */>,
    class = decltype(U{ static_cast<value_type>(std::declval<Elements>())... })>

我想根据std::is_constructible检查我的第一个条件,以便我可以将此检查移至enable_if。这可能吗?我已尝试使用std::is_constructible_v<U, decltype(static_cast<value_type>(std::declval<Elements>()))...>,但这似乎与以前的检查不同。

例如,foo<bar<3>>{1, 2, 3};

template<std::size_t N>
struct bar
{
    using value_type = double;
    double data[N];
};

将使用上一次检查进行编译,但会产生新错误。

2 个答案:

答案 0 :(得分:1)

如上所述Rostislav,如果T不是函数类型,std::is_constructible_v<T, Args>true iff变量定义T obj(std::declval<Args>()...);格式正确。 bar<1> obj(0.);中的情况并非如此,因为bar<1>没有相应的构造函数。

相比之下,bar1<1> obj{ 0. };格式正确。使用建议的Detection Toolkit,我们可以使用

template<class T, typename... Arguments>
using initializable_t = decltype(T{ std::declval<Arguments>()... });

template<class T, typename... Arguments>
constexpr bool is_initializable_v = is_detected_v<initializable_t, T, Arguments...>;

并将支票更改为

template<class... Elements, class U = Tuple,
    class = std::enable_if_t<is_initializable_v<U, decltype(static_cast<value_type>(std::declval<Elements>()))...>>>

我认为这比简单的decltype方法更具可读性。

答案 1 :(得分:0)

is_constructible特征表现不同的原因是确实没有bar的构造函数将三个int值作为参数。在这种特定情况下,使用聚合初始化。

但是你可以通过使用一个简单的帮助enable_if将测试放入struct

#include <type_traits>

template<typename T>
struct test : std::true_type
{
};

template<class Tuple>
struct foo
{
    using value_type = typename Tuple::value_type;

    template<class... Elements, class U = Tuple,
        class = std::enable_if_t<
            test<decltype(U{ static_cast<value_type>(std::declval<Elements>())... })>::value>
            /* && your other tests */
            >
    foo(Elements&&... elements)
        : m_elements{ static_cast<value_type>(std::forward<Elements>(elements))... }
    {}

    Tuple m_elements;
};


template<std::size_t N>
struct bar
{
    using value_type = double;
    double data[N];
};

int main()
{
    foo<bar<3>>{1, 2, 3};
}

LIVE

或者,您可以使用this问题的任何答案创建类型特征,将decltype魔法推送到单独的位置。