繁体   English   中英

std::enable_if 具有多个 or 条件

[英]std::enable_if with multiple or-conditions

我正在努力寻找允许多个 OR'ed std::enable_if条件用于我的模板函数的正确语法。

#include <type_traits>

template <typename T>
using Foo = std::enable_if_t<std::is_same_v<T, int>>;

template <typename T>
using Bar = std::enable_if_t<std::is_same_v<T, float>>;

// Fine - accepts only ints
template <typename T, typename = Foo<T>>
void FooIt(T t) {}

// Fine - accepts only floats
template <typename T, typename = Bar<T>>
void BarIt(T t) {}

template <typename T,
          // This does not work
          typename = std::enable_if_t<Bar<T>::value || Foo<T>::value>>
void FooOrBarIt(T t) {}

int main() {
  FooIt(1);
  BarIt(1.0f);

  // FooIt(1.0f);  // OK - No float version of FooIt
  // BarIt(1);     // OK - No int version of BarIt

  // Not OK - Both fails to compile
  // FooOrBarIt(1);
  // FooOrBarIt(1.0f);
}

我希望FooOrBarIt function 通过结合前面的FooBar条件接受int s 和float s 但没有别的。

请注意,我想避免改变以前的条件,我想按原样组合它们。 任何 C++17 都可以。

当前代码因编译错误而失败,例如:

candidate template ignored: requirement 'std::is_same_v<int, float>' was not satisfied [with T = int]

在 clang。

您正在尝试在enable_if_t内部使用enable_if_t ,这不是您所需要的。 您需要在 1 enable_if_t中使用is_same_v ,例如:

template <typename T,
          typename = std::enable_if_t<std::is_same_v<T,float> || std::is_same_v<T,int>>>

因此,相应地调整您的using语句,例如:

#include <type_traits>

template <typename T>
inline constexpr bool Is_Int = std::is_same_v<T, int>;

template <typename T>
using Enable_If_Int = std::enable_if_t<Is_Int<T>>;

template <typename T>
inline constexpr bool Is_Float = std::is_same_v<T, float>;

template <typename T>
using Enable_If_Float = std::enable_if_t<Is_Float<T>>;

// Fine - accepts only ints
template <typename T, typename = Enable_If_Int<T>>
void FooIt(T t) {}

// Fine - accepts only floats
template <typename T, typename = Enable_If_Float<T>>
void BarIt(T t) {}

template <typename T,
          typename = std::enable_if_t<Is_Float<T> || Is_Int<T>>>
void FooOrBarIt(T t) {}

int main() {
    FooIt(1);
    BarIt(1.0f);

    // FooIt(1.0f);  // OK - No float version of FooIt
    // BarIt(1);     // OK - No int version of BarIt

    // OK - Both compile fine
    FooOrBarIt(1);
    FooOrBarIt(1.0f);
}

在线演示

SFINAE 不像逻辑运算符那样安静地工作。 您应该使用std::disjunction及其对应的std::conjunction来组合 SFINAE 的逻辑测试。 这些在 C++17 中引入,因此您应该能够将它们与您的设置一起使用。

你的|| 运算符适用于bool类型。

您要查找的bool类型已存在于您的代码中。 std::is_same_v始终是bool.

template <typename T, typename = std::enable_if_t<
           std::is_same_v<T,int> || std::is_same_v<T,float>
         >>* = nullptr

考虑到同时包含Foo<T>Bar<T>的表达式永远无法明确定义。

你正在做货物崇拜 SFINAE。 通过这个,我的意思是你正在使用 SFINAE 模式来实现你需要的东西,而不是理解你为什么要做这些模式。

这通常并没有那么糟糕。 SFINAE 有点疯狂,因为它是一种偶然的特性,它泄漏到语言中并为我们提供了图灵完备的重载解决方案。 我的意思是,上次我检查过,它与模板 class 专业化一起工作的事实实际上并不在标准中。

因此,遵循现有模式使其看起来合理是一个很好的计划。

然而,当你想做一些新的事情时,你必须了解实际发生的事情。

template <typename T>
using Foo = std::enable_if_t<std::is_same_v<T, int>>;

这是一个模板别名,如果T通过测试,则返回类型void ,如果T未通过测试,则返回类型失败。

// Fine - accepts only ints
template <typename T, typename = Foo<T>>
void FooIt(T t) {}

你的评论有点不对。 FooIt<double, void>在这里工作正常。

这里发生的事情是,如果T不是int ,您的默认类型参数会产生替换失败。 如果Tint ,则第二个类型参数的默认值为void

template <typename T,
      // This does not work
      typename = std::enable_if_t<Bar<T>::value ||  Foo<T>::value>>
void FooOrBarIt(T t) {}

当然不是。

类型void上没有::value Bar<T> void或替换失败,对于Foo<T>也是如此。

都不支持::value 所以你得到一个错误。

您的Foo<T>Bar<T>不是很好的模式。 但是你说你想保留它们。

所以我要开始的是:

template<class T, template<class>class Z, class=void, template<class>class...Zs>
struct test{};
template<class T, template<class>class Z>
struct test<T,Z,Z<T>>{
  using type=void;
};
template<class T,
  template<class>class Z0,
  template<class>class Z1, 
  template<class>class...Zs
>
struct test<T, Z0, Z0<T>, Z1, Zs...>:
  test<T, Z1, void, Zs...>
{};
template<class T, template<class>class Z0, template<class>class...Zs>
using test_t = typename test<T, Z0, void, Zs...>::type;

现在test_t可以用来引信你的特征。

template <typename T,
      typename = test_t<T, Bar, Foo>>
void FooOrBarIt(T t) {}

以你熟悉的方式。

题外话

请注意,这种风格的 SFINAE 存在一些问题,您无法使用它在FooOrBarIt的两个重载之间进行调度。 IE,

template<class T, class=Bar<T>>
void bob(T) {}
template<class T, class=Foo<T>>
void bob(T) {}

这会导致重载解析错误。

另一种货物崇拜模式:

template<class T>
using Bar = std::enable_if_t<std::is_same_v<T,int>, bool>;
template<class T>
using Foo = std::enable_if_t<std::is_same_v<T,double>, bool>;

template<class T, Bar<T> =true>
void bob(T) {}
template<class T, Foo<T> =true>
void bob(T) {}

看起来略有不同,但允许上面的两个bob重载工作。

对于该变体,您必须更改加入它们的方式:

template<Ts...>
using AllTraits = bool;

template <typename T,
      AllTraits<Foo<T>, Bar<T>> = true>
void FooOrBarIt(T t) {}

请注意,这个特定的 cargo cult 版本中的=true不是测试; =false会产生完全相同的结果。

为了提供对比,C++20 版本:

#include <type_traits>

template<typename T>
concept Foo = std::is_same_v<T, int>;

template<typename T>
concept Bar = std::is_same_v<T, float>;

template<typename T>
concept FooOrBar = Foo<T> || Bar<T>;

template<Foo T>
void FooIt(T) { }

template<Bar T>
void BarIt(T) { }

template<FooOrBar T>
void FooOrBarIt(T) { }

int main() {
  FooIt(1); // OK
  BarIt(1.0f); // OK
  
  // FooIt(1.0f);  // OK - No float version of FooIt
  // BarIt(1);     // OK - No int version of BarIt


  FooOrBarIt(1); // OK
  FooOrBarIt(1.0f); // OK
  // FooOrBarIt(1.0); // No double version of FooOrBarIt
}

以这种方式组合概念要容易得多。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM