简体   繁体   English

std::enable_if 具有多个 or 条件

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

I am struggling to find the correct syntax for allowing multiple OR'ed std::enable_if conditions for my template functions.我正在努力寻找允许多个 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);
}

I want the FooOrBarIt function to accept both int s and float s but nothing else by combining the previous Foo and Bar conditions.我希望FooOrBarIt function 通过结合前面的FooBar条件接受int s 和float s 但没有别的。

Note that I want to avoid changing the previous conditions, I want to combine them as is.请注意,我想避免改变以前的条件,我想按原样组合它们。 Anything C++17 is fine.任何 C++17 都可以。

The current code fails with compile errors like:当前代码因编译错误而失败,例如:

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

in clang.在 clang。

You are trying to use enable_if_t inside of enable_if_t , which is not what you need.您正在尝试在enable_if_t内部使用enable_if_t ,这不是您所需要的。 You need to use is_same_v inside of 1 enable_if_t , eg:您需要在 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>>>

So adjust your using statements accordingly, eg:因此,相应地调整您的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);
}

Online Demo在线演示

SFINAE doesn't work quiet as simply with logical operators. SFINAE 不像逻辑运算符那样安静地工作。 You should look to use std::disjunction and its counterpart std::conjunction to combine logical tests for SFINAE.您应该使用std::disjunction及其对应的std::conjunction来组合 SFINAE 的逻辑测试。 These where introduced with C++17, so you should be able to use them with the setup you have.这些在 C++17 中引入,因此您应该能够将它们与您的设置一起使用。

Your ||你的|| operator works on bool types.运算符适用于bool类型。

The bool types you are looking for already exist in your code.您要查找的bool类型已存在于您的代码中。 std::is_same_v is always a 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

Consider that an expression containing both Foo<T> and Bar<T> can never be well-defined.考虑到同时包含Foo<T>Bar<T>的表达式永远无法明确定义。

You are doing cargo-cult SFINAE.你正在做货物崇拜 SFINAE。 By this, I mean you are using SFINAE patterns to pull off what you need, and not understanding why you are doing those patterns.通过这个,我的意思是你正在使用 SFINAE 模式来实现你需要的东西,而不是理解你为什么要做这些模式。

This usually isn't all that bad.这通常并没有那么糟糕。 SFINAE is sort of insane, as an accidental feature that leaked into the language and gave us Turing-complete overload resolution. SFINAE 有点疯狂,因为它是一种偶然的特性,它泄漏到语言中并为我们提供了图灵完备的重载解决方案。 I mean, last I checked, the fact it worked with template class specialization isn't actually in the standard.我的意思是,上次我检查过,它与模板 class 专业化一起工作的事实实际上并不在标准中。

So following existing patterns to make it look reasonable is a good plan.因此,遵循现有模式使其看起来合理是一个很好的计划。

When you want to do something new, however, you have to understand what is actually going on.然而,当你想做一些新的事情时,你必须了解实际发生的事情。

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

this is a template alias that results in the type void if T passes the test, and in a substitution failure if T fails the test.这是一个模板别名,如果T通过测试,则返回类型void ,如果T未通过测试,则返回类型失败。

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

your comment is a bit wrong.你的评论有点不对。 FooIt<double, void> works fine here. FooIt<double, void>在这里工作正常。

What is going on here is that your default type argument produces a substitution failure if T is not an int .这里发生的事情是,如果T不是int ,您的默认类型参数会产生替换失败。 If T is an int , the default value for the 2nd type argument is void .如果Tint ,则第二个类型参数的默认值为void

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

of course it doesn't.当然不是。

There is no ::value on the type void .类型void上没有::value Bar<T> is either void or a substitution failure, and the same for Foo<T> . Bar<T> void或替换失败,对于Foo<T>也是如此。

Neither supports ::value .都不支持::value So you get an error.所以你得到一个错误。

Your Foo<T> and Bar<T> are not a great pattern.您的Foo<T>Bar<T>不是很好的模式。 But you stated you want to keep them.但是你说你想保留它们。

So what I'd start with is this:所以我要开始的是:

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;

now test_t can be used to fuze your traits.现在test_t可以用来引信你的特征。

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

in a way you are familiar with.以你熟悉的方式。

Digression题外话

Note that this style of SFINAE has problems where you can't use it to dispatch between two overloads of FooOrBarIt .请注意,这种风格的 SFINAE 存在一些问题,您无法使用它在FooOrBarIt的两个重载之间进行调度。 Ie, IE,

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

this results in an overload resolution error.这会导致重载解析错误。

Another cargo-cult pattern:另一种货物崇拜模式:

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) {}

looks slightly different, but permits the two bob overloads above to work.看起来略有不同,但允许上面的两个bob重载工作。

For that variant, you'd have to change how you joined them:对于该变体,您必须更改加入它们的方式:

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

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

note that the =true in this particular cargo cult version is not a test;请注意,这个特定的 cargo cult 版本中的=true不是测试; =false would produce the exact same results. =false会产生完全相同的结果。

For the sake of providing a contrast, a C++20 version:为了提供对比,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
}

Concepts are a lot easier to combine in this fashion.以这种方式组合概念要容易得多。

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

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