很好的说法" foo不在{bar,baz}"在C ++ 11/14中

时间:2016-06-17 17:21:35

标签: c++ c++11 c++14

我正在编写C ++而忽略了Python的清晰度。但是我知道C ++已经在不断发展,并且想知道是否有更好的方式来做这样的事情:

if (foo != bar && foo != baz)

在Python中我会这样做:

if foo not in {bar, baz}:

C ++ 11或C ++ 14中是否有一个奇特的功能允许我做类似的可读操作?

编辑:很多人都在想我为什么要这么短的替换。我没有,但我不想让我的例子像原始代码一样丑陋和难以理解。它更像是:

if (somelongvariablename.somelongmethodname() !=
    SomeReallyLongNamespace::AndAnotherSubClassname::A_LONG_CONSTANT_NAME &&
    somelongvariablename.somelongmethodname() !=
    SomeReallyLongNamespace::AndAnotherSubClassname::ANOTHER_LONG_CONSTANT_NAME) {
    // foo

8 个答案:

答案 0 :(得分:10)

这样的事情怎么样:

#include <type_traits>
#include <tuple>
#include <utility>

template <typename ...Args> struct InT: std::tuple<Args...>
{
    template <typename ...Brgs>
    explicit InT(Brgs &&... brgs)
    : std::tuple<Args...>(std::forward<Brgs>(brgs)...) {}

    template <typename T, std::size_t ...I>
    bool noteq(T && t, std::index_sequence<I...>) const
    {
        return (true && ... && (t != std::get<I>(*this)));
    }
};

template <typename ...Args>
InT<Args &&...> AnyOf(Args &&... args)
{
    return InT<Args &&...>(std::forward<Args>(args)...);
}

template <typename T, typename ...Args>
bool operator!=(T && t, InT<Args...> in)
{
    return in.noteq(std::forward<T>(t), std::index_sequence_for<Args...>());
}

用法:

if (x != AnyOf(1, 3, 5)) { f(); }

答案 1 :(得分:4)

我们可以使用以下语法:

int main() {
  if (foo *in* std::tie(bar, baz)) {
  }
}

live example

它也适用于*in*右侧的C数组或std容器等。

优化器进入后,这应该是零开销。

否定只是:

  if (!(foo *in* std::tie(bar, baz)))
因为我不认为特殊情况是一个好计划。如果您需要foo *not in* std::tie(bar, baz))语法,请参阅此帖子的底部。

首先,命名运算符库:

namespace named_operator {
  template<class D>struct make_operator{constexpr make_operator(){}};

  template<class T, char, class O> struct half_apply { T&& lhs; };

  template<class Lhs, class Op>
  half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
    return {std::forward<Lhs>(lhs)};
  }

  template<class Lhs, class Op, class Rhs>
  auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
  -> decltype( invoke( std::declval<Lhs>(), Op{}, std::declval<Rhs>() ) )
  {
    return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
  }
}

大约12行,使命名运算符变得容易。

现在我们创建一个命名运算符。     namespace my_ns {       constexpr struct in_tag:{}中的named_operator :: make_operator {};     }     使用my_ns :: in; 它需要一个动作。 C ++ 17版本很简单:

namespace my_ns {
  // foo in tuple support:
  template<class T, class...Args>
  bool invoke( T const& lhs, in_tag, std::tuple<Args...> const& rhs ) {
    return std::apply( [&](auto&&...args){
      return (false || ... || (lhs == args));
    }, rhs);
  }
  // foo in container support:
  template<class T, class Container>
  bool invoke( T const& lhs, in_tag, Container const& rhs ) {
    using std::begin; using std::end;
    auto it = std::find( begin(rhs), end(rhs), lhs );
    return it != end(rhs);
  }
}

C ++ 11元组支持版本有点棘手,因为缺少std::apply和折叠扩展:

namespace my_ns {
  // tuple support:
  template<class T, class...Args, std::size_t...Is>
  bool invoke( T const& lhs, in_tag, std::tuple<Args...> const& rhs, std::index_sequence<Is...> ) {
    bool retval = false;
    using discard=int[];
    (void)discard{ 0,(void(
      retval = retval || (lhs == std::get<Is>(rhs))
    ),0)... };
    return retval;
  }
  template<class T, class...Args>
  bool invoke( T const& lhs, in_tag, std::tuple<Args...> const& rhs ) {
    return invoke(lhs, in_tag{}, rhs, std::index_sequence_for<Args...>{} );
  }
  // container support is identical to C++17 version
}

如上所述,如果你想要

  if (foo *not in* std::tie(bar, baz))

我能做到。

添加not_in运算符:

namespace my_ns {
  constexpr struct not_in_tag:named_operator::make_operator<not_in_tag> {} not_in {};
}
using my_ns::not_in;

然后我们定义在它们之间切换的!

namespace my_ns {
  constexpr not_in_tag operator!(in_tag){return {};}
  constexpr in_tag operator!(not_in_tag){return {};}
|

以及not_in运算符的作用:

namespace my_ns {
  template<class T, class Rhs>
  bool invoke( T const& lhs, not_in_tag, Rhs const& rhs ) {
    return !invoke(lhs, in_tag{}, rhs );
  }
}

代表invoke

现在我们得到

  if (foo *not in* std::tie(bar, baz)) {
    std::cout << "foo not in {bar,baz}\n";
  }
  if (foo *not in* std::make_tuple(bar, baz, 3)) {
    std::cout << "foo not in {bar,baz, 3}\n";
  }

  if (foo *not_in* std::tie(bar, baz)) {
    std::cout << "foo not in {bar,baz}\n";
  }

  if (foo *!in* std::tie(bar, baz)) {
    std::cout << "foo not in {bar,baz}\n";
  }

无论你想要什么。

live example

答案 2 :(得分:3)

我强烈建议您不要使用花哨的东西污染代码,除非需要。

以下C ++ 14解决方案提供了Python中的中缀语法,以防您有两个以上的值进行比较:

#include <tuple>
#include <utility>

template <typename... T>
struct in_checker : std::tuple<T...> {
    using std::tuple<T...>::tuple;
    template <typename U, std::size_t... Is>
    constexpr bool contains(U const& u, std::index_sequence<Is...>) const {
        for (auto b : {std::get<Is>(*this) == u...})
            if (b) return true;
        return false;
    }
    template <typename U>
    constexpr bool contains(U const& u) const {
        return contains(u, std::index_sequence_for<T...>{});}
};

template <typename U, typename... T>
constexpr bool operator==(U const& u, in_checker<T...> const& in) {
    return in.contains(u);}
template <typename U, typename... T>
constexpr bool operator!=(U const& u, in_checker<T...> const& in) {
    return !(u == in);}

template <typename... T>
constexpr in_checker<T...> in(T const&... t) {return std::tie(t...);}

#include <iostream>
int main() {
    int t = 2;
    if (t == in(1, 2, 3))
        std::cout << "Congrats";
    if (t != in(1, 3, 4))
        std::cout << "... again!";
}

Demo

答案 3 :(得分:2)

我想,不是很优雅,但你可以写一个简单的isIn()模板函数

template <typename T>
bool isIn (const T & val, const std::set<T> & s)
 { return s.cend() != s.find(val); }

以下是Tint

时使用的简单案例
#include <set>
#include <iostream>

template <typename T>
bool isIn (const T & val, const std::set<T> & s)
 { return s.cend() != s.find(val); }

int main ()
 {
   int  bar = 5;
   int  baz = 3;

   int  foo = 0;

   if ( false == isIn(foo, {bar, baz}) )
      std::cout << foo << " isn\'t in set" << std::endl;
   else
      std::cout << foo << " is in set" << std::endl;

   foo = 3;

   if ( false == isIn(foo, {bar, baz}) )
      std::cout << foo << " isn\'t in set" << std::endl;
   else
      std::cout << foo << " is in set" << std::endl;

   return 0;
 }

---发布编辑编辑 -

@guidoism:我认为你的问题以更一般的方式很有趣,但是,如果你只能针对枚举值检查somelongmethodname(),我认为可读的解决方案可能是好的switch

 using SomeReallyLongNamespace::AndAnotherSubClassname;

 switch ( somelongvariablename.somelongmethodname() )
  {
     case A_LONG_CONSTANT_NAME:
     case ANOTHER_LONG_CONSTANT_NAME:
        // do nothing (or something, if you like)
        break;

     default:
        // do something
        break;
  }

答案 4 :(得分:1)

我确实喜欢Python语法的优雅简洁性:

if foo not in {bar, baz}

在脚本语言中,简约代码是最高的优点。但是在这条线路的引导下:

  1. 构建一个类似于C ++ initializer_list
  2. 的简单容器
  3. 将对barbaz的引用插入容器
  4. 获取对容器
  5. 中的1 st 项的引用
  6. 比较对foo的引用,如果相等则销毁容器并跳转到false分支
  7. 获取容器中的下一个引用
  8. 比较对foo的引用,如果相等则销毁容器并跳转到false分支
  9. 销毁容器并进入true分支
  10. 在C ++中,最高的好处是速度,所以让我们看看我们在C ++中被迫使用它的方式:

    if(foo != bar && foo != baz)
    
    1. 如果相等跳转到foo分支
    2. ,则将barfalse进行比较
    3. 如果相等跳转到foo分支
    4. ,则将bazfalse进行比较
    5. 进入true分支
    6. 这并不是说好的编译器无法在Python if语句中优化容器,但如果这些变量代表具有不同构造规则的对象,那么这些对象可能几乎不可能。如果C ++确实使用这种语法为我们提供支持,那么我将成为第二个采用它的人,就在你之后。但是现在构建一个临时容器来掩盖个别比较是一个糟糕的决定,原因有两个:

      <强> 1。容器结构的意外成本可能无法优化  2.由于没有标准提供的关键字,读者弄清楚发生了什么的成本将超过使用容器所获得的优雅。
      所以现在最好的解决方案仍旧可靠:if(foo != bar && foo != baz)

      那并不是说我们不能在C ++中使用容器,因为foobarbaz的类型是int你可以做任何一个这些伟大的邪恶:

      1. if(basic_string<int>{bar, baz}.find(foo) == -1)
      2. if(set<int>{bar, baz}.insert(foo).second)
      3. if(!set<int>{bar, baz}.count(foo))
      4. 修改

        在看到您对问题的编辑之后,应该说即使您可以使用仅保存您的Python语法: characters-in - foo + 4个字符给出代码:

        if (somelongvariablename.somelongmethodname() != SomeReallyLongNamespace::AndAnotherSubClassname::A_LONG_CONSTANT_NAME &&
            somelongvariablename.somelongmethodname() != SomeReallyLongNamespace::AndAnotherSubClassname::ANOTHER_LONG_CONSTANT_NAME)
        

        如果你的意思是你有publicstatic这样的变量:

        namespace SomeReallyLongNamespace {
            struct AndAnotherSubClassname{
                static const auto A_LONG_CONSTANT_NAME = 13;
                static const auto ANOTHER_LONG_CONSTANT_NAME = 42;
            };
        }
        

        然后using - 语句将消除大量的输入,不仅仅是在这里,而是在using被定义的范围内的所有地方:

        using namespace SomeReallyLongNamespace;
        
        if (somelongvariablename.somelongmethodname() != AndAnotherSubClassname::A_LONG_CONSTANT_NAME &&
            somelongvariablename.somelongmethodname() != AndAnotherSubClassname::ANOTHER_LONG_CONSTANT_NAME)
        

        接下来假设somelongvariablename.somelongmethodname()是一个const方法,最佳做法是在常量临时中镜像它的返回值,因此只需要调用一次方法,再次改进我们的代码:

        using SomeReallyLongNamespace::AndAnotherSubClassname;
        const auto& foo = somelongvariablename.somelongmethodname();
        
         if(foo != AndAnotherSubClassname::A_LONG_CONSTANT_NAME && foo != AndAnotherSubClassname::ANOTHER_LONG_CONSTANT_NAME)
        

        那里显然有一些条件,但是我会说,如果你可以解决这个问题,那么你的问题就会大大改善你的代码,以至于你只需要保存 7个字符 Python语法,将旧的忠实C ++语法返回给可行的竞争者。

答案 5 :(得分:1)

#include <iostream>
#include <initializer_list>
using namespace std;
template<typename T>
initializer_list<T> in(initializer_list<T> K)
{
    return K;
}
template<typename T>
bool operator !=(T A, initializer_list<T> B)
{
    bool R = true;
    for (auto E : B)R = R && (A != E);
    return R;
}

int main()
{
    if (1 != in({2,3,4}))
    {
        cout << "Wow" << endl;
    }
    return 0;
}

通过这种方式,我们可以使代码更具可读性 1 != in({2,3,4})

编辑:

找到一种更易读的方式。

#include <iostream>
#include <initializer_list>
using namespace std;
template<typename T>
initializer_list<T> o(initializer_list<T> K)
{
    return K;
}
class not_i
{

};
class not_i_helper1
{
public:
    int S;
};
not_i_helper1 operator<(int A, not_i P)
{
    not_i_helper1 K;
    K.S = A;
    return K;
}
bool operator>(not_i_helper1 S, initializer_list<int> B)
{
    bool R = true;
    for (auto E : B)R = R && (S.S != E);
    return R;
}
not_i not_in;
int main()
{
    if (1 < not_in > o({ 2,3,4 }))
    {
        cout << "Success!" << endl;
    }
    else
    {
        cout << "Huh?" << endl;
    }
    return 0;
}

现在我们可以使用1 < not_in > o({ 2,3,4 })

答案 6 :(得分:0)

如果您的案例非常简单,您可以这样做:

for (auto& e : {bar, baz})
  if (foo == e)
  { /* found */ };

请注意,这样就无法说明是否在那里。

答案 7 :(得分:-1)

这是我15“16:9笔记本电脑上的单行:D

#include <iostream>
#include <set>

int main() {
    for(std::set<int> omg({1, 2, 3, 4}); omg.find(42) == omg.cend(); ) { std::cout << "There's no Answer." << std::endl; break; }
}