C ++ template type inference problem

motivation: I would like to create a utility class so that instead of writing:

if( someVal == val1 || someVal == val2 || someVal == val3 ) 

Instead, I could write:

 if( is(someVal).in(val1, val2, val3) ) 

which is much closer to math, a is an element of (b, c, d) ', and will also save a lot of messages when the variable name' someVal 'is long.

Here is the code that I still have (for 2 and 3 values):

 template<class T> class is { private: T t_; public: is(T t) : t_(t) { } bool in(const T& v1, const T& v2) { return t_ == v1 || t_ == v2; } bool in(const T& v1, const T& v2, const T& v3) { return t_ == v1 || t_ == v2 || t_ == v3; } }; 

However, it does not compile if I write:

 is(1).in(3,4,5); 

instead I have to write

 is<int>(1).in(3,4,5); 

This is not so bad, but it would be better if somehow the compiler could understand that the type is int , and I need to explicitly specify it.
Is there anyway to do this, or am I stuck specifying it explicitly?

+4
source share
6 answers

If you want to keep this syntax, you can use a helper function, for example:

 template<class T> class is_op { private: T t_; public: is_op(T t) : t_(t) { } bool in(const T& v1, const T& v2) { return t_ == v1 || t_ == v2; } bool in(const T& v1, const T& v2, const T& v3) { return t_ == v1 || t_ == v2 || t_ == v3; } }; template< class U > inline is_op<U> is( U const& v ) { return is_op<U>( v ); } int main(int argc, char* argv[]) { is( 1 ).in( 1 , 2 , 4 ); } 
+14
source

The problem is pretty funny, though Boolean conditions can be hairy.

I myself prefer to write special functions, because the meaning here is difficult to convey. What is he doing:

 if (someVal == val1 || someVal == val2 || someVal == val3) 

mean?

Is an

 if ( is(someval).in(val1, val2, val3) ) // or if ( is(someval).in(val1)(val2)(val3) ) // implements short-circuiting // and removes arity issue // using a proxy object 

it's better?

I think it would be easier to read:

 bool isToBeLogged(const Foo& foo) { // Either static std::set<Foo> ValuesToLog = /* some boost assign magic or whatever */; return ValuesToLog.find(foo) != ValuesToLog.end(); // Or return foo == val1 || foo == val2 || foo == val3; } if (isToBeLogged(someVal)) 

I think this is a matter of style.

Advantages of the second method, including:

  • If the test is executed more than once, the logic is not scattered throughout the volume (therefore, the changes are simple)
  • No need to comment on the test, the method name already does this

Discomfort? I guess it prints more ... oh well: p

+5
source
 template<typename T> is<T> is_value(T value) { return is<T>(value); } int main() { bool r ; r = is_value(1).in(3,4,5); r = is_value(3).in(3,4,5); return 0; } 
+1
source

As for the general problem, a useful function, for example, contains, it may be convenient:

 #include <boost/range.hpp> template <class Range, class T> bool contains(const Range& range, const T& value) { return std::find(boost::begin(range), boost::end(range), value) != boost::end(range); } 

(Using Boost also allows you to accept arrays, although you can write this overload separately. It can also be overloaded for containers using the find member function.)

In C ++ 0x, this can be extended to support std::initializer_list<T> *, which allows a pretty enjoyable use:

 if (contains({1, 2, 3}, value) {...} 

* Not sure if it shouldn't work already, but my compiler requires an overload to make it work.

+1
source

You are stuck in it - the constructor requires the type to be provided as a template parameter. And I must say that I really don't like your idea (especially the name of the class). Why not use std :; set? Or even a template function - something like:

 template <typename T> bool IsIn( T v, T a, T b, T c ) { ... } 
0
source

The modification for your comparison class is possibly using varargs to make it common to n elements of the collection.

0
source

Source: https://habr.com/ru/post/1306100/


All Articles