我正在编写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
答案 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)) {
}
}
它也适用于*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";
}
无论你想要什么。
答案 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!";
}
答案 3 :(得分:2)
我想,不是很优雅,但你可以写一个简单的isIn()
模板函数
template <typename T>
bool isIn (const T & val, const std::set<T> & s)
{ return s.cend() != s.find(val); }
以下是T
为int
#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}
在脚本语言中,简约代码是最高的优点。但是在这条线路的引导下:
initializer_list
bar
和baz
的引用插入容器foo
的引用,如果相等则销毁容器并跳转到false
分支foo
的引用,如果相等则销毁容器并跳转到false
分支true
分支在C ++中,最高的好处是速度,所以让我们看看我们在C ++中被迫使用它的方式:
if(foo != bar && foo != baz)
foo
分支bar
与false
进行比较
foo
分支baz
与false
进行比较
true
分支这并不是说好的编译器无法在Python if
语句中优化容器,但如果这些变量代表具有不同构造规则的对象,那么这些对象可能几乎不可能。如果C ++确实使用这种语法为我们提供支持,那么我将成为第二个采用它的人,就在你之后。但是现在构建一个临时容器来掩盖个别比较是一个糟糕的决定,原因有两个:
<强> 1。容器结构的意外成本可能无法优化
2.由于没有标准提供的关键字,读者弄清楚发生了什么的成本将超过使用容器所获得的优雅。
所以现在最好的解决方案仍旧可靠:if(foo != bar && foo != baz)
那并不是说我们不能在C ++中使用容器,因为foo
,bar
和baz
的类型是int
你可以做任何一个这些伟大的邪恶:
if(basic_string<int>{bar, baz}.find(foo) == -1)
if(set<int>{bar, baz}.insert(foo).second)
if(!set<int>{bar, baz}.count(foo))
修改强>
在看到您对问题的编辑之后,应该说即使您可以使用仅保存您的Python语法: characters-in - foo
+ 4个字符给出代码:
if (somelongvariablename.somelongmethodname() != SomeReallyLongNamespace::AndAnotherSubClassname::A_LONG_CONSTANT_NAME &&
somelongvariablename.somelongmethodname() != SomeReallyLongNamespace::AndAnotherSubClassname::ANOTHER_LONG_CONSTANT_NAME)
如果你的意思是你有public
,static
这样的变量:
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; }
}