473,320 Members | 1,820 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

Trying to apply SFINAE

Hi,

I'm having two overloaded function templates,

#include <iterator>

template< typename T >
void test( T /*a1*/, T /*a2*/ ) {}

template< typename Iter >
void test( Iter /*b*/, Iter /*e*/ ) {}

which I need to call. (In reality, these are constructors,
in case that matters.) Unfortunately, the compiler isn't
as clever and can't tell 'T' from 'Iter', so I have to
give it a hint whether what gets passed is an iterator.
But my attempt to use SFINAE for this

template< typename Iter, class ItTr >
void test( Iter /*b*/, Iter /*e*/
, ItTr = std::iterator_traits<Iter>() ) {}

doesn't work, since this completely removed the iterator
overload from the set the compiler considered. Or that's
what I figured because this

#include <iterator>

//template< typename T >
//void test( T /*a1*/, T /*a2*/ ) {}

template< typename Iter, class ItTr >
void test( Iter /*b*/, Iter /*e*/
, ItTr = std::iterator_traits<Iter>() ) {}

int main()
{
const int fa[] = { 255, 255, 255, 255 };

//test(0,1);
test(fa, fa+4);

return 0;
}

doesn't compile.
Now I'm stumped. I thought I had done this before, so I am
probably making something stupid here. (It's already passed
6pm here...)

Anyone out there?

TIA,

Schobi
Sep 29 '08 #1
11 1483
Hendrik Schober wrote:
[..]
#include <iterator>

//template< typename T >
//void test( T /*a1*/, T /*a2*/ ) {}

template< typename Iter, class ItTr >
void test( Iter /*b*/, Iter /*e*/
, ItTr = std::iterator_traits<Iter>() ) {}

int main()
{
const int fa[] = { 255, 255, 255, 255 };

//test(0,1);
test(fa, fa+4);

return 0;
}

doesn't compile.
[..]
Well, the compiler cannot deduce the type from a default argument, I
vaguely recall that it's a non-deducible context. That's why putting
the traits there won't cut it. Consider

#include <iterator>

template<typename Iter>
void test(Iter, Iter, std::iterator_traits<Iterconst* = 0);

int main ()
{
const int fa[] = { 1,2,3,4 };
test(fa, fa+4);
}

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Sep 29 '08 #2
Victor Bazarov wrote:
Hendrik Schober wrote:
[...]

Well, the compiler cannot deduce the type from a default argument, I
vaguely recall that it's a non-deducible context. That's why putting
the traits there won't cut it. Consider

#include <iterator>

template<typename Iter>
void test(Iter, Iter, std::iterator_traits<Iterconst* = 0);

int main ()
{
const int fa[] = { 1,2,3,4 };
test(fa, fa+4);
}
Thanks! However, while the above works, this doesn't:

#include <iterator>

template< typename T >
void test( T /*a1*/, T /*a2*/ ) {}

template< typename Iter >
void test( Iter /*b*/, Iter /*e*/
, std::iterator_traits<Iter= std::iterator_traits<Iter>() ) {}

int main()
{
const int fa[] = { 255, 255, 255, 255 };

test(0,1);
test(fa, fa+4);

return 0;
}

(Both VC and Comeau complain it's ambiguous.) But that's
just what I need.
V
Schobi
Sep 30 '08 #3
James Kanze wrote:
On Sep 29, 6:20 pm, Hendrik Schober <spamt...@gmx.dewrote:
>I'm having two overloaded function templates,
> #include <iterator>
> template< typename T >
void test( T /*a1*/, T /*a2*/ ) {}
> template< typename Iter >
void test( Iter /*b*/, Iter /*e*/ ) {}
>which I need to call. (In reality, these are constructors,
in case that matters.)

It could be critical, if you need the initialization list.
The problem is more with your below idea: I cannot
(easily) forward calls.
[...]
But of course, since it's undefined behavior to instantiate
iterator_trais with anything that is neither an iterator nor a
pointer, you couldn't count on this even if the type deduction
trick worked.
Is it?
I thought std lib implementors must have faced this,
but now I see that this

#include <vector>

int main()
{
const unsigned int ua[] = { 255, 255, 255, 255 };

std::vector<intv1(0u,1u);
std::vector<intv2(ua, ua+4);

return 0;
}

fails spectacularly with Dinkumware (both call the ctor
taking iterators), so I must be wrong.

Well, it seems I'm back to square one, then.
Any ideas out there?

Schobi
Sep 30 '08 #4
Hi to all,
maybe something like the following might work.
Relying on the fact that an iterator should be either
- a pointer
- a class with some inner typedefs (here iterator_category)
The inspector class can be customized to better suit your needs...
Maybe there is a simpler way... don't know :-)
Bye,
Francesco

// code
#include <iterator>
#include <iostream>
#include <vector>

// ala boost::enable_if
template< typename T, bool K >
class CTypeEnable;

template< typename T >
class CTypeEnable< T, false >
{};

template< typename T >
class CTypeEnable< T, true >
{
public:
typedef T tResult;
};

// ala boost::??
template< typename T >
struct CIsIter
{
typedef char (&tYes)[1];
typedef char (&tNo)[2];

template< typename T2 >
static typename CTypeEnable< tYes,
sizeof( typename T2::iterator_category ) >::tResult
Check( T2 * );

template< typename T2 >
static tYes Check( T2 ** );

static tNo Check( ... );

enum { kResult = sizeof( Check( (T*) NULL ) )
== sizeof( tYes ) };
};

// SFINAE with return type

template< typename T >
typename CTypeEnable< void, !CIsIter< T >::kResult >::tResult F( T )
{ std::cout << "NO ITERATOR\n"; }

template< typename T >
typename CTypeEnable< void, CIsIter< T >::kResult >::tResult F( T )
{ std::cout << "ITERATOR\n"; }

struct A
{};

int main()
{
F( 10 );
F( ( int*)NULL);
std::vector< int vec;
F( vec.begin() );
F( A() );
}
//end code

Hendrik Schober ha scritto:
James Kanze wrote:
On Sep 29, 6:20 pm, Hendrik Schober <spamt...@gmx.dewrote:
I'm having two overloaded function templates,
#include <iterator>
template< typename T >
void test( T /*a1*/, T /*a2*/ ) {}
template< typename Iter >
void test( Iter /*b*/, Iter /*e*/ ) {}
which I need to call. (In reality, these are constructors,
in case that matters.)
It could be critical, if you need the initialization list.

The problem is more with your below idea: I cannot
(easily) forward calls.
[...]
But of course, since it's undefined behavior to instantiate
iterator_trais with anything that is neither an iterator nor a
pointer, you couldn't count on this even if the type deduction
trick worked.

Is it?
I thought std lib implementors must have faced this,
but now I see that this

#include <vector>

int main()
{
const unsigned int ua[] = { 255, 255, 255, 255 };

std::vector<intv1(0u,1u);
std::vector<intv2(ua, ua+4);

return 0;
}

fails spectacularly with Dinkumware (both call the ctor
taking iterators), so I must be wrong.

Well, it seems I'm back to square one, then.
Any ideas out there?

Schobi
Sep 30 '08 #5
Hendrik Schober wrote:
Victor Bazarov wrote:
>Hendrik Schober wrote:
[...]

Well, the compiler cannot deduce the type from a default argument, I
vaguely recall that it's a non-deducible context. That's why putting
the traits there won't cut it. Consider

#include <iterator>

template<typename Iter>
void test(Iter, Iter, std::iterator_traits<Iterconst* = 0);

int main ()
{
const int fa[] = { 1,2,3,4 };
test(fa, fa+4);
}

Thanks! However, while the above works, this doesn't:

#include <iterator>

template< typename T >
void test( T /*a1*/, T /*a2*/ ) {}

template< typename Iter >
void test( Iter /*b*/, Iter /*e*/
, std::iterator_traits<Iter= std::iterator_traits<Iter>()
) {}

int main()
{
const int fa[] = { 255, 255, 255, 255 };

test(0,1);
test(fa, fa+4);

return 0;
}

(Both VC and Comeau complain it's ambiguous.) But that's
just what I need.
They are probably correct :-/

Let's try to involve a class where you can make a partial specialisation:

template<class T, boolstruct ActualWorker; // "abstract"
template<class Tstruct ActualWorker<T,false// non-iterator
{
static void test(T /*a1*/, T /*a2*/) { /*whatever*/ }
};

template<class Tclass ActualWorker<T,true// iterator
{
static void test(T /*i1*/, T /*i2*/) { /*whatever*/ }
};

// now - how do we determine it's an iterator?
template<class Tstruct IsIterator { enum { yes = 0 }; };
template<class Tstruct IsIterator {
... // here you need to add some way to set 'yes' to 1
... // if 'T' is an iterator. It's up to you to define
... // what is an iterator and what isn't.
};

template<class Tvoid test(T t1, T t2) {
return ActualWorker<T, IsIterator<T>::yes >::test(t1, t2);
}

int main()
{
test(42, 666);
int foo[] = { 1,2,3,4 };
test(foo, foo+4);
}

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Sep 30 '08 #6
On Sep 30, 11:58 am, Hendrik Schober <spamt...@gmx.dewrote:
James Kanze wrote:
On Sep 29, 6:20 pm, Hendrik Schober <spamt...@gmx.dewrote:
I'm having two overloaded function templates,
#include <iterator>
template< typename T >
void test( T /*a1*/, T /*a2*/ ) {}
template< typename Iter >
void test( Iter /*b*/, Iter /*e*/ ) {}
which I need to call. (In reality, these are constructors,
in case that matters.)
It could be critical, if you need the initialization list.
The problem is more with your below idea: I cannot
(easily) forward calls.
That's why I raised the issue of initialization lists. That's
the main reason I can think of why you might not be able to
forward calls. (It is, in fact, the only reason I can think of
why you might not be able to forward calls.)
[...]
But of course, since it's undefined behavior to instantiate
iterator_trais with anything that is neither an iterator nor a
pointer, you couldn't count on this even if the type deduction
trick worked.
Is it?
I think so. In §17.4.3.6/2, it says:

In particular, the effects are undefined in the
following cases:
[...]
-- for types used as template arguments when
instantiating a template component, if the
operations on the type do not implement the
semantics of the applicable Requirements
subclause.[...]

I'm not sure, of course, because the description of Iterator
traits (§24.3.1) doesn't actually contain a Requirements
subclause. It does require a specific implementation, however
(with a partial specialization for pointers), and that
implementation uses typename Iterator::difference_type, etc. So
the presence of those types in the instantiation type would seem
to be a requirement. (In this regared, the definition of
iterator_category is probably the most significant; I can't
imagine anything but an iterator defining it.)

In practice, what I would expect is that the code would fail to
compile *IF* you use such an instantiation in a context which
requires a complete definition. Or, if that failure occured
during template type deduction, SFINAE. But I couldn't get it
to work.
I thought std lib implementors must have faced this,
but now I see that this
#include <vector>
int main()
{
const unsigned int ua[] = { 255, 255, 255, 255 };
std::vector<intv1(0u,1u);
std::vector<intv2(ua, ua+4);
return 0;
}
fails spectacularly with Dinkumware (both call the ctor
taking iterators), so I must be wrong.
The second is required to call the constructor taking iterators.
The first shouldn't, however; the standard says that if the
iterator type (determined by type deduction) is an integral
type, the constructor shall has the same effect as:

X( static_cast< typename X::size_type >( f ),
static_cast< typename X::value_type >( l ) ) ;

(This leads to some interesting, and possibly unintentional
effects, due to the fact that static_cast is an explicit
conversion. Thus:

std::vector< std::vector< int v( 10, 30 ) ;

works, although there is no implicit conversion of 30 to
std::vector< int >.)

The version of VC++ (which uses Dinkumware) which I have access
to gets this right. Some older implementations of the library
(Dinkumware or others), designed for compilers which didn't
support member templates, may have problems, however.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Oct 1 '08 #7
Victor Bazarov wrote:
Hendrik Schober wrote:
[...]
>>
(Both VC and Comeau complain it's ambiguous.) But that's
just what I need.

They are probably correct :-/

Let's try to involve a class where you can make a partial specialisation:
[example snipped]
I knew how to solve this with introducing forwarding functions
or additional class templates. I didn't want to change the code
so much.
Thanks nevertheless!
V
Schobi
Oct 1 '08 #8
James Kanze wrote:
On Sep 30, 11:58 am, Hendrik Schober <spamt...@gmx.dewrote:
>James Kanze wrote:
>>On Sep 29, 6:20 pm, Hendrik Schober <spamt...@gmx.dewrote:
[...]
>>>which I need to call. (In reality, these are constructors,
in case that matters.)
>>It could be critical, if you need the initialization list.
> The problem is more with your below idea: I cannot
(easily) forward calls.

That's why I raised the issue of initialization lists. That's
the main reason I can think of why you might not be able to
forward calls. (It is, in fact, the only reason I can think of
why you might not be able to forward calls.)
Ah, mea culpa. I didn't get this.
>>[...]
But of course, since it's undefined behavior to instantiate
iterator_trais with anything that is neither an iterator nor a
pointer, you couldn't count on this even if the type deduction
trick worked.
> Is it?

I think so. In §17.4.3.6/2, it says:
[explanation snipped]
Thanks!
> I thought std lib implementors must have faced this,
but now I see that this
> #include <vector>
> int main()
{
const unsigned int ua[] = { 255, 255, 255, 255 };
> std::vector<intv1(0u,1u);
std::vector<intv2(ua, ua+4);
> return 0;
}
> fails spectacularly with Dinkumware (both call the ctor
taking iterators), so I must be wrong.

[...]

The version of VC++ (which uses Dinkumware) which I have access
to gets this right. Some older implementations of the library
(Dinkumware or others), designed for compilers which didn't
support member templates, may have problems, however.
I tried with VC9 (2008) and VC7.1 (2003). Both /seemed/ to fail,
but now that I dug deeper, I know that, while it indeed calls
the wrong ctor, that in turn forwards to some other function
and (by use of iterator tags) picks an overload of that which
does the right thing.
I'm sorry for spreading false information. :-x

Schobi
Oct 1 '08 #9
Francesco wrote:
Hi to all,
maybe something like the following might work.
[a lot of clever code snipped]
It might (except that ctors don't have a return type, but
that's curable), but it would introduce a lot of template
meta stuff into the code. This whole thing only came up
because I eliminated a bunch of warnings and I'm reluctant
to change too much well-tested code (I'd have to change
dozens of classes) at the heart of a quite sophisticated
domain-specific library I'm not very familiar with yet just
to get rid of a few warnings.
Thanks anyway. Yours is an interesting solution. It does
remind me that I still have to lobby for boost ('enable_if')
here. :)

Schobi
Oct 1 '08 #10
Francesco wrote:
Hi to all,
maybe something like the following might work.
Relying on the fact that an iterator should be either
- a pointer
- a class with some inner typedefs (here iterator_category)
The inspector class can be customized to better suit your needs...
Maybe there is a simpler way... don't know :-)
Bye,
Francesco

// code
#include <iterator>
#include <iostream>
#include <vector>

// ala boost::enable_if
template< typename T, bool K >
class CTypeEnable;

template< typename T >
class CTypeEnable< T, false >
{};

template< typename T >
class CTypeEnable< T, true >
{
public:
typedef T tResult;
};

// ala boost::??
template< typename T >
struct CIsIter
{
typedef char (&tYes)[1];
typedef char (&tNo)[2];

template< typename T2 >
static typename CTypeEnable< tYes,
sizeof( typename T2::iterator_category ) >::tResult
Check( T2 * );

template< typename T2 >
static tYes Check( T2 ** );

static tNo Check( ... );

enum { kResult = sizeof( Check( (T*) NULL ) )
== sizeof( tYes ) };
};

// SFINAE with return type

template< typename T >
typename CTypeEnable< void, !CIsIter< T >::kResult >::tResult F( T )
{ std::cout << "NO ITERATOR\n"; }

template< typename T >
typename CTypeEnable< void, CIsIter< T >::kResult >::tResult F( T )
{ std::cout << "ITERATOR\n"; }

struct A
{};

int main()
{
F( 10 );
F( ( int*)NULL);
std::vector< int vec;
F( vec.begin() );
F( A() );
}
//end code

That idea can work a little simpler:
struct yes_type { char dummy; };
struct no_type { yes_type a; yes_type b; };

template < typename T >
struct is_iterator_type {

template < typename S >
static
yes_type check ( S*, typename S::iterator_category* ptr = 0 );

template < typename S >
static
yes_type check ( S** );

static
no_type check ( ... );

public:

static bool const value =
sizeof( check((T*)0) ) == sizeof( yes_type );

};
#include <iostream>
#include <vector>

template < typename T >
void show ( void ) {
if ( is_iterator_type<T>::value ) {
std::cout << "is iterator type\n";
} else
std::cout << "is not iterator type\n";
}

#define SHOW(T) \
std::cout << #T << " "; \
show<T>();

int main() {
SHOW( std::vector<int>::const_iterator );
SHOW( std::vector<int);
SHOW( int* );
SHOW( int );
}
Best

Kai-Uwe Bux

Oct 5 '08 #11
Kai-Uwe Bux wrote:
Francesco wrote:
[...]

That idea can work a little simpler:

[code snipped]
In my case it's even simpler, since I don't need the meta-function.
Here's what I came up with while commuting this morning:

struct yes_type { char dummy; };
struct no_type { char dummy1; char dummy2; };

template < typename T >
yes_type is_iterator(const T&, typename T::iterator_category* = 0)
{return yes_type();}

template < typename T >
yes_type is_iterator(const T* const)
{return yes_type();}

no_type is_iterator(const void* const)
{return no_type();}

no_type is_iterator(...)
{return no_type();}
#include <iostream>
#include <vector>

template < typename T >
inline void show( const T&, yes_type /*is_iterator*/)
{std::cout << "iterator: ";}

template < typename T >
inline void show( const T&, no_type /*is_iterator*/)
{std::cout << "no iterator: ";}

template < typename T >
inline void show(const char* str) {
show( T(), typename is_iterator(T()) );
std::cout << str << '\n';
}

#define SHOW(T) show<T>(#T)

int main() {
SHOW( int* );
SHOW( int** );
SHOW( int* const* );
SHOW( const int* );
SHOW( const int** );
SHOW( const int* const* );
SHOW( std::vector<int>::const_iterator );
std::cout << '\n';
SHOW( void* );
SHOW( std::vector<int);
SHOW( int );
}

However, this still needs a forwarding function... <sighs>
Kai-Uwe Bux
Schobi
Oct 6 '08 #12

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

8
by: Peter Collingbourne | last post by:
Hello I am trying to do some template metaprogramming involving enabling or disabling a method in a parameterised class based on some condition. I am trying to use the Boost enable_if mechanism...
2
by: Clark S. Cox III | last post by:
I'm writing a class that, depending on a template parameter, has constructors that take differing numbers of arguments. I initially thought that I could use SFINAE (via boost::enable_if_c) to...
4
by: kaalus | last post by:
Is it possible to use SFINAE to provide different implementations of some function depending on the fact that operator << is overloaded for some type? For example: template<class T> void...
4
by: Gianni Mariani | last post by:
Below is an attempt to factorize the "SFINAE" idiom. I have tried it on three compilers and they all complain. As far as I can tell, it's valid code. The questions are, are the compilers right...
5
by: Fei Liu | last post by:
Hello, I just hit a strange problem regarding SFINAE. The following code causes compile error (void cannot be array element type), I thought SFINA should match test(...) version instead and not...
35
by: James Kanze | last post by:
Just ran into an interesting question concerning SFINAE. Given the following code: #include <iostream> #include <typeinfo> template< typename T > class P { public:
0
by: DolphinDB | last post by:
The formulas of 101 quantitative trading alphas used by WorldQuant were presented in the paper 101 Formulaic Alphas. However, some formulas are complex, leading to challenges in calculation. Take...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.