【问题标题】:Type trait to check that all types in a parameter pack are copy constructible类型特征以检查参数包中的所有类型是否都是可复制构造的
【发布时间】:2015-06-18 15:32:44
【问题描述】:

我需要一个类型特征来检查参数包中的所有类型是否都是可复制构造的。这是我到目前为止所做的。 main 函数包含一些测试用例,用于检查功能。

#include <type_traits>
#include <string>
#include <memory> 

template <class... Args0toN>
struct areCopyConstructible;

template<>
struct areCopyConstructible<> : std::true_type {};

template <class Arg0, class... Args1toN, class std::enable_if< !std::is_copy_constructible<Arg0>::value>::type* = nullptr >
struct areCopyConstructible : std::false_type {};

template <class Arg0, class... Args1toN, class std::enable_if< std::is_copy_constructible<Arg0>::value>::type* = nullptr >
struct areCopyConstructible : areCopyConstructible<Args1toN...> {};

int main()
{
  static_assert(areCopyConstructible<>::value, "failed");
  static_assert(areCopyConstructible<int>::value, "failed");
  static_assert(areCopyConstructible<int, std::string>::value, "failed");
  static_assert(!areCopyConstructible<std::unique_ptr<int> >::value, "failed");
  static_assert(!areCopyConstructible<int, std::unique_ptr<int> >::value, "failed");
  static_assert(!areCopyConstructible<std::unique_ptr<int>, int >::value, "failed");
}

Link to Live Example

我的想法是递归检查包的头部元素是否可复制构造,然后继续使用尾部。不幸的是,我没有编译这个想法。我对可变参数模板的了解不是很高级。我猜,模板列表中的参数包后的 enable-if 不起作用。我不知道。有没有人有好的建议,如何解决这个问题?

【问题讨论】:

标签: c++ templates c++11 typetraits


【解决方案1】:

首先定义一个可重用的实用程序来测试包中的每个谓词是否为真:

template<typename... Conds>
  struct and_
  : std::true_type
  { };

template<typename Cond, typename... Conds>
  struct and_<Cond, Conds...>
  : std::conditional<Cond::value, and_<Conds...>, std::false_type>::type
  { };

然后将它与is_copy_constructible(或任何其他一元类型特征)一起使用是微不足道的:

template<typename... T>
  using areCopyConstructible = and_<std::is_copy_constructible<T>...>;

像这样定义and_ 的一个优点是它会短路,即在第一个错误结果之后停止为包的其余部分实例化is_copy_constructible

【讨论】:

  • 非常优雅的解决方案。 C++14 std::conditional_t 可以省去输入::type
【解决方案2】:

我更喜欢@Columbo 的bool_pack 技巧。首先是一个模板来测试bool 参数包中的所有内容是否为true

template<bool...> struct bool_pack;
template<bool... bs> 
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;

然后

template<class... Ts>
using areCopyConstructible = all_true<std::is_copy_constructible<Ts>::value...>;

【讨论】:

  • 这非常清晰和简单,但是对所有is_copy_constructible 专业化进行了热切的评估。稍微复杂一点的解决方案只评估所需的数量以找到答案,即短路。
  • @JonathanWakely 我仍然相信这总体上更有效 - 使用包递归是非常低效的 IIRC。幸运的是,这场辩论将在 C++1Z 中使用折叠表达式结束。
  • @Columbo,是的,关于折叠表达式的要点。 is_copy_constructible&lt;T&gt;::value 可能必须实例化大量代码,包括基类和 T 的成员,以确定类型是否具有可访问的复制构造函数,因此避免这样做可能是好的(尽管它取决于类型)。我的and_ 类型非常简单,尽管它使用递归。
  • @JonathanWakely 我认为这个特征最常见的用例是验证一组类型在static_assert 中是否都是可复制构造的,因此它通常会用 all-copy- 实例化可构造类型,eager 和 lazy 之间的区别消失了。
  • @T.C.好点子。我们在整个 libstdc++ 中使用 and_ 之类的东西来处理更一般的用例,并且在 SFINAE 约束中,并不期望一切都正确,但对于 OP 的情况,短路可能不会提供任何好处。
【解决方案3】:

如果从std::true_typestd::false_type 的继承并不重要,那么这可以在没有SFINAE 的情况下以直接的方式完成:

template <class... Args0toN>
struct areCopyConstructible;

template<>
struct areCopyConstructible<> : std::true_type {};

template <class Arg0, class... Args1toN>
struct areCopyConstructible<Arg0, Args1toN...> {
    static constexpr bool value = std::is_copy_constructible<Arg0>::value
        && areCopyConstructible<Args1toN...>::value;
};

如果要从std::true_typestd::false_type继承,可以使用std::conditional

template <class... Args0toN>
struct areCopyConstructible;

template<>
struct areCopyConstructible<> : std::true_type {};

template <class Arg0, class... Args1toN>
struct areCopyConstructible<Arg0, Args1toN...> : 
    std::conditional<std::is_copy_constructible<Arg0>::value,
        areCopyConstructible<Args1toN...>,
        std::false_type
    >::type
{};

【讨论】:

  • 值得一提的是,与 OP 不同的是,您无需费心从 false_typetrue_type 派生,除非在微不足道的情况下。用户可能不需要那个,但我在 SO 上看到了在重载函数参数中使用 false_typetrue_type 的代码,所以这样的事情不会起作用。不过,如果需要,它很容易添加。
【解决方案4】:

我知道这是一个老问题,但由于我们很快就会有 C++17,我鼓励看看 std::conjunction。有了它,你可以写出这样的东西

template <typename ...Args>
using areCopyConstructible = typename std::conjunction<std::is_copy_constructible<Args>...>::type;

【讨论】:

  • 注:是的,std::conjunction 基于我在答案中显示的and_ 模板。
  • 注:除了使用bool_constant&lt;conjunction_v&lt;...&gt;&gt;,你可以使用conjunction&lt;...&gt;::type,它已经是bool_constant,或者更简单的conjunction&lt;...&gt;(它是从bool_constant派生的)。
  • 注:使用别名模板而不是定义新的类模板在编译时开销更少。
  • 几个小时前我发布了几乎相同的东西,所以一旦我意识到它,我就删除了我的答案并从here链接到这个
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多