decltype in template param list?

N

Noah Roberts

Trying to come up with a method to find out if a class contains a name
within it that is callable with certain variables, whether it's a
templated function or not.

Here's my attempt:

template < typename T > T&& declval(); // MSVC doesn't have.

template < typename T, typename Enable = void >
struct has_callable_f
: boost::mpl::false_
{};

template < typename T >
struct has_callable_f<T, decltype(declval<T>().f(declval<int>()))>
: boost::mpl::true_
{};

struct test
{
template < typename T >
void f() {}
};

int main()
{
static_assert(has_callable_f<test>::value, "blah");
}

I get an error indicating the decltype expression is invalid:


1>d:\dev_workspace\experimental\2010scratch\2010scratch\scratch.cpp(20):
error C2228: left of '.f' must have class/struct/union
1> type is 'T'
1>

If I put 'test' instead of 'T' (moving the declaration of course) then
it works.

Is what I'm trying to do legitimate?
 
V

Victor Bazarov

Trying to come up with a method to find out if a class contains a name
within it that is callable with certain variables, whether it's a
templated function or not.

Here's my attempt:

template < typename T > T&& declval(); // MSVC doesn't have.

Name was confusing to me so I changed it to 'xval' in my attempts.
template < typename T, typename Enable = void >
struct has_callable_f
: boost::mpl::false_

Oh, your code has no 'boost' headers... Please post *complete* code next
time. Thanks!
{};

template < typename T >
struct has_callable_f<T, decltype(declval<T>().f(declval<int>()))>

The line above is the reason I changed your 'declval' to 'xval' - too
much "decl". BTW, your 'test::f' does not have any arguments, is that
intentional?
: boost::mpl::true_
{};

struct test
{
template < typename T >
void f() {}
};

int main()
{
static_assert(has_callable_f<test>::value, "blah");
}

I get an error indicating the decltype expression is invalid:


1>d:\dev_workspace\experimental\2010scratch\2010scratch\scratch.cpp(20):
error C2228: left of '.f' must have class/struct/union
1> type is 'T'
1>

If I put 'test' instead of 'T' (moving the declaration of course) then
it works.

Is what I'm trying to do legitimate?

I'm still trying to get my head around your problem, sorry for not being
helpful so far. I'm gonna dig around a bit and will get back to the
group with what I find, OK?

V
 
V

Victor Bazarov

Name was confusing to me so I changed it to 'xval' in my attempts.


Oh, your code has no 'boost' headers... Please post *complete* code next
time. Thanks!


The line above is the reason I changed your 'declval' to 'xval' - too
much "decl". BTW, your 'test::f' does not have any arguments, is that
intentional?


I'm still trying to get my head around your problem, sorry for not being
helpful so far. I'm gonna dig around a bit and will get back to the
group with what I find, OK?

I've dug around a bit, and so far this is doesn't work as expected:
-------------------------------------
template < bool v > struct bool_ { enum { yesno = v }; };
typedef bool_<false> false_;
typedef bool_<true> true_;

template < typename T, typename U = void >
struct has_nested_type : false_
{
};

template < typename T >
struct has_nested_type<T, typename T::nested > : true_
{
};

struct test
{
typedef int nested;
};

struct noftest
{
};

int main()
{
static_assert(has_nested_type<test>::yesno, "test: BAD");
static_assert(has_nested_type<noftest>::yesno, "noftest: BAD");
}
-------------------------------------
which leads me to believe that either VC++ is not up to snuff yet on
instantiating the templates the way we think they ought to be
instantiated (so I'd like somebody to try that code with another C++0x
compliant compiler), or there is something we're missing with the way
the partial specialisation is supposed to be chosen over the generic
one. BTW, I tried it with Comeau online, and it also failed on both
assertions, which doesn't prove it's right, just makes me feel that I
need to dig more thoroughly around partial specialisations and their
instantiation.

V
 
N

Noah Roberts

The name is standard. MSVC simply doesn't implement it.
I've dug around a bit, and so far this is doesn't work as expected:
-------------------------------------
template < bool v > struct bool_ { enum { yesno = v }; };
typedef bool_<false> false_;
typedef bool_<true> true_;

template < typename T, typename U = void >
struct has_nested_type : false_
{
};

template < typename T >
struct has_nested_type<T, typename T::nested > : true_
{
};

struct test
{
typedef int nested;
};

struct noftest
{
};

int main()
{
static_assert(has_nested_type<test>::yesno, "test: BAD");
static_assert(has_nested_type<noftest>::yesno, "noftest: BAD");
}
-------------------------------------
which leads me to believe that either VC++ is not up to snuff yet on
instantiating the templates the way we think they ought to be
instantiated (so I'd like somebody to try that code with another C++0x
compliant compiler), or there is something we're missing with the way
the partial specialisation is supposed to be chosen over the generic
one. BTW, I tried it with Comeau online, and it also failed on both
assertions, which doesn't prove it's right, just makes me feel that I
need to dig more thoroughly around partial specialisations and their
instantiation.

I believe this fails because the type of the default parameter doesn't
match the type you supply in the specialization.

The line, has_nested_type<test> results in the compiler attempting to
instantiate has_nested_type<test,void>. The specialization results in
has_nested_type<test,int>. This doesn't match so it uses the
non-specialized case.

My problem seems to be specifically the fact that the decltype
expression in that location seems to confuse the compiler. The program
fails to compile whether or not the specialization is ever used.
Interestingly, I am able to use the exact same expression in a function
overload matcher:

template < typename T >
struct has_callable_f
{
typedef char (&no) [1];
typedef char (&yes) [2];

template < typename S >
static yes check(decltype(declval<S>().f(declval<int>())) *);

template < typename S >
static no check(...);

enum { value = sizeof(check<T>(nullptr)) == sizeof(yes) };

};
 
V

Victor Bazarov

The name is standard. MSVC simply doesn't implement it.
I've dug around a bit, and so far this is doesn't work as expected:
-------------------------------------
template < bool v > struct bool_ { enum { yesno = v }; };
typedef bool_<false> false_;
typedef bool_<true> true_;

template < typename T, typename U = void >
struct has_nested_type : false_
{
};

template < typename T >
struct has_nested_type<T, typename T::nested > : true_
{
};

struct test
{
typedef int nested;
};

struct noftest
{
};

int main()
{
static_assert(has_nested_type<test>::yesno, "test: BAD");
static_assert(has_nested_type<noftest>::yesno, "noftest: BAD");
}
-------------------------------------
which leads me to believe that either VC++ is not up to snuff yet on
instantiating the templates the way we think they ought to be
instantiated (so I'd like somebody to try that code with another C++0x
compliant compiler), or there is something we're missing with the way
the partial specialisation is supposed to be chosen over the generic
one. BTW, I tried it with Comeau online, and it also failed on both
assertions, which doesn't prove it's right, just makes me feel that I
need to dig more thoroughly around partial specialisations and their
instantiation.

I believe this fails because the type of the default parameter doesn't
match the type you supply in the specialization.

The line, has_nested_type<test> results in the compiler attempting to
instantiate has_nested_type<test,void>. The specialization results in
has_nested_type<test,int>. This doesn't match so it uses the
non-specialized case.

My problem seems to be specifically the fact that the decltype
expression in that location seems to confuse the compiler. The program
fails to compile whether or not the specialization is ever used.
Interestingly, I am able to use the exact same expression in a function
overload matcher:

template < typename T >
struct has_callable_f
{
typedef char (&no) [1];
typedef char (&yes) [2];

template < typename S >
static yes check(decltype(declval<S>().f(declval<int>())) *);

template < typename S >
static no check(...);

enum { value = sizeof(check<T>(nullptr)) == sizeof(yes) };

};

After some playing around I basically came to a similar conclusion: VC++
implements 'decltype' sensitively to the context... Oh well, maybe
they're going to get it right in a couple of versions.

V
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top