【问题标题】:conjunction type traits in C++11C++11 中的连词类型特征
【发布时间】:2020-11-23 12:37:01
【问题描述】:

我需要检查std::enable_if 中的可变参数:

使用 C++17 我会写:

template <typename A, typename ...B>
class Foo : public A, public B...
{

public:

    template <typename = std::enable_if_t<std::is_default_constructible_v<A> &&
        (std::is_default_constructible_v<B> && ...)>>
    Foo()
    {}
    
    Foo(A &&a, B && ... b)
       : A(std::forward<A>(a)),
       B(std::forward<B>(b))...
    {}

};

但是 C++11 没有这种扩展参数包的特性。它也不提供std::conjunction

结合C++11有什么简单的实现方式? 我想带有递归的 SFINAE 就足够了,但我无法绕过它。

【问题讨论】:

标签: c++ c++11 variadic-templates sfinae


【解决方案1】:

试图从不同的角度看待问题...

如果我没记错的话,std::tuple 是默认可构造的,当且仅当)每种类型的元组都是默认可构造的,那么

 template <typename
    = typename std::enable_if<std::is_default_constructible<std::tuple<A, B...>::value>::type>
 Foo()
  { }

也可以

 template <typename = decltype(std::tuple<A, B...>())>
 Foo()
  { }

?

【讨论】:

  • 不错,虽然它只支持default_constructible,而不支持一般的conjunction
  • 在这种特定情况下,您不必声明自己的conjunction。这是一个更好的解决方案,特别是找出所有类型是否都是可构造的。尽管它适合我的特定目标,但我发现您的回答非常吸引人。但总的来说,问题是关于连词本身
  • @SergeyKolesnik - 我添加了答案只是为了显示针对您的特定问题的可能替代解决方案,但您是对的:不是您确切问题的答案。
  • @max66 是对具体问题的一个很好的回答:)
【解决方案2】:

您需要一个工具来对可变参数进行合取操作。

#include <iostream>
#include <type_traits>

template<class...> struct conjunction : std::true_type { };
template<class B1> struct conjunction<B1> : B1 { };
template<class B1, class... Bn>
struct conjunction<B1, Bn...> 
    : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};

template <typename A, typename ...B>
class Foo : public A, public B...
{

public:

    template <typename std::enable_if<std::is_default_constructible<A>::value &&
        conjunction<std::is_default_constructible<B>...>::value, bool>::type = true>
    Foo()
    {}
    
    Foo(A &&a, B && ... b)
       : A(std::forward<A>(a)),
       B(std::forward<B>(b))...
    {}

};

struct A {};
struct B {};
struct C {
    C(int x) {}
};

int main()
{
    Foo<A, B> foo;
    //Foo<A, B, C> bar;

    return 0;
}

https://godbolt.org/z/d3jvM4

基于 C++17 中可用的 https://en.cppreference.com/w/cpp/types/conjunction

【讨论】:

  • 不要考虑使用默认模板参数,而是使用非类型默认参数。默认类型参数不被视为函数签名的一部分,因此您不能在不给它们不同签名的情况下重载使用 SFINAE 的其他模板。你可以改用typename std::enable_if&lt;std::is_default_constructible&lt;A&gt;::value &amp;&amp; conjunction&lt;std::is_default_constructible&lt;B&gt;...&gt;::value, bool&gt;::type = true
  • 我会将递归移动到一个懒惰评估的上下文中。我相信大多数编译器最终以 O(n^2) 模板名称长度用上述设计实例化(C(a,b,c) 实例化 C(b,c) 实例化 C(c))。当然,这仅在您关心构建时间或 N 很大时才重要。
猜你喜欢
  • 2023-04-04
  • 1970-01-01
  • 2021-09-14
  • 2010-12-03
  • 2012-04-30
  • 2013-03-13
  • 2012-03-18
  • 2011-11-28
相关资源
最近更新 更多