Template template class predicate not working in partial specialization
up vote
7
down vote
favorite
I have many EnableIf
traits that basically check whether the input type satisfies an interface. I was trying to create a generic Resolve
trait that can be used to transform those into a boolean trait.
Something like this - https://wandbox.org/permlink/ydEMyErOoaOa60Jx
template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};
Now if you have an EnableIf
trait like so
template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;
You can create a boolean version of that very quickly
template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};
Or the analogous variable template.
But for some reason the partial specialization is not working as expected. Resolve does not work as intended. See the output here - https://wandbox.org/permlink/ydEMyErOoaOa60Jx. The same thing implemented "manually" works - https://wandbox.org/permlink/fmcFT3kLSqyiBprm
I am resorting to manually defining the types myself. Is there a detail with partial specializations and template template arguments that I am missing?
c++ templates c++17 sfinae enable-if
add a comment |
up vote
7
down vote
favorite
I have many EnableIf
traits that basically check whether the input type satisfies an interface. I was trying to create a generic Resolve
trait that can be used to transform those into a boolean trait.
Something like this - https://wandbox.org/permlink/ydEMyErOoaOa60Jx
template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};
Now if you have an EnableIf
trait like so
template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;
You can create a boolean version of that very quickly
template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};
Or the analogous variable template.
But for some reason the partial specialization is not working as expected. Resolve does not work as intended. See the output here - https://wandbox.org/permlink/ydEMyErOoaOa60Jx. The same thing implemented "manually" works - https://wandbox.org/permlink/fmcFT3kLSqyiBprm
I am resorting to manually defining the types myself. Is there a detail with partial specializations and template template arguments that I am missing?
c++ templates c++17 sfinae enable-if
1
I cannot find the reason, but you need the partial specialization to beResolve<Predicate, T, std::void_t<Predicate<T>>
. Then at that point, you can also remove thestd::void_t
in the alias.
– Guillaume Racicot
Nov 28 at 6:47
btw, your code look a lot like the dectection idiom
– Guillaume Racicot
Nov 28 at 6:49
@GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
– Curious
Nov 28 at 6:49
1
It can be an alias template to simplydecltype
. I don't know why, but thevoid_t
must be in the partial specialization. I think it has to do with how partial ordering work.
– Guillaume Racicot
Nov 28 at 6:52
add a comment |
up vote
7
down vote
favorite
up vote
7
down vote
favorite
I have many EnableIf
traits that basically check whether the input type satisfies an interface. I was trying to create a generic Resolve
trait that can be used to transform those into a boolean trait.
Something like this - https://wandbox.org/permlink/ydEMyErOoaOa60Jx
template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};
Now if you have an EnableIf
trait like so
template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;
You can create a boolean version of that very quickly
template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};
Or the analogous variable template.
But for some reason the partial specialization is not working as expected. Resolve does not work as intended. See the output here - https://wandbox.org/permlink/ydEMyErOoaOa60Jx. The same thing implemented "manually" works - https://wandbox.org/permlink/fmcFT3kLSqyiBprm
I am resorting to manually defining the types myself. Is there a detail with partial specializations and template template arguments that I am missing?
c++ templates c++17 sfinae enable-if
I have many EnableIf
traits that basically check whether the input type satisfies an interface. I was trying to create a generic Resolve
trait that can be used to transform those into a boolean trait.
Something like this - https://wandbox.org/permlink/ydEMyErOoaOa60Jx
template <
template <typename...> class Predicate,
typename T,
typename = std::void_t<>>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, Predicate<T>> : std::true_type {};
Now if you have an EnableIf
trait like so
template <typename T>
using EnableIfHasFoo = std::void_t<decltype(std::declval<T>().foo())>;
You can create a boolean version of that very quickly
template <typename T>
struct HasFoo : Resolve<EnableIfHasFoo, T> {};
Or the analogous variable template.
But for some reason the partial specialization is not working as expected. Resolve does not work as intended. See the output here - https://wandbox.org/permlink/ydEMyErOoaOa60Jx. The same thing implemented "manually" works - https://wandbox.org/permlink/fmcFT3kLSqyiBprm
I am resorting to manually defining the types myself. Is there a detail with partial specializations and template template arguments that I am missing?
c++ templates c++17 sfinae enable-if
c++ templates c++17 sfinae enable-if
edited Nov 28 at 6:43
asked Nov 28 at 6:38
Curious
11.8k22470
11.8k22470
1
I cannot find the reason, but you need the partial specialization to beResolve<Predicate, T, std::void_t<Predicate<T>>
. Then at that point, you can also remove thestd::void_t
in the alias.
– Guillaume Racicot
Nov 28 at 6:47
btw, your code look a lot like the dectection idiom
– Guillaume Racicot
Nov 28 at 6:49
@GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
– Curious
Nov 28 at 6:49
1
It can be an alias template to simplydecltype
. I don't know why, but thevoid_t
must be in the partial specialization. I think it has to do with how partial ordering work.
– Guillaume Racicot
Nov 28 at 6:52
add a comment |
1
I cannot find the reason, but you need the partial specialization to beResolve<Predicate, T, std::void_t<Predicate<T>>
. Then at that point, you can also remove thestd::void_t
in the alias.
– Guillaume Racicot
Nov 28 at 6:47
btw, your code look a lot like the dectection idiom
– Guillaume Racicot
Nov 28 at 6:49
@GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
– Curious
Nov 28 at 6:49
1
It can be an alias template to simplydecltype
. I don't know why, but thevoid_t
must be in the partial specialization. I think it has to do with how partial ordering work.
– Guillaume Racicot
Nov 28 at 6:52
1
1
I cannot find the reason, but you need the partial specialization to be
Resolve<Predicate, T, std::void_t<Predicate<T>>
. Then at that point, you can also remove the std::void_t
in the alias.– Guillaume Racicot
Nov 28 at 6:47
I cannot find the reason, but you need the partial specialization to be
Resolve<Predicate, T, std::void_t<Predicate<T>>
. Then at that point, you can also remove the std::void_t
in the alias.– Guillaume Racicot
Nov 28 at 6:47
btw, your code look a lot like the dectection idiom
– Guillaume Racicot
Nov 28 at 6:49
btw, your code look a lot like the dectection idiom
– Guillaume Racicot
Nov 28 at 6:49
@GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
– Curious
Nov 28 at 6:49
@GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
– Curious
Nov 28 at 6:49
1
1
It can be an alias template to simply
decltype
. I don't know why, but the void_t
must be in the partial specialization. I think it has to do with how partial ordering work.– Guillaume Racicot
Nov 28 at 6:52
It can be an alias template to simply
decltype
. I don't know why, but the void_t
must be in the partial specialization. I think it has to do with how partial ordering work.– Guillaume Racicot
Nov 28 at 6:52
add a comment |
2 Answers
2
active
oldest
votes
up vote
5
down vote
I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t
, here's an interesting explanation
Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.
template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};
template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());
live on compiler explorer
Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
– StoryTeller
Nov 28 at 7:02
@StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
– Guillaume Racicot
Nov 28 at 7:06
Might be. I'm examining partial ordering at the moment to see why.
– StoryTeller
Nov 28 at 7:08
@StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
– Guillaume Racicot
Nov 28 at 7:12
Oh I think I know why. Let me edit
– Guillaume Racicot
Nov 28 at 7:13
|
show 2 more comments
up vote
5
down vote
The reason why your approach will fail is that Predicate<T>>
in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.
You can wrap your Predicate<T>>
to a non-deduced context to make it work:
template<class T>
struct identity {
using type = T;
};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};
Live Demo
Because inside a non-deduced context, the deduction won't happen for Predicate<T>
part, instead, it will use the Predicate
and T
obtained from elsewhere.
As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t
as a template alias, will be replaced by void
in deduction phase (see [temp.alias]/2), thus no deduction will happen.
Here are some examples to illustrate it more clearly:
template<class T>
using always_int = int;
template<template<class> class TT>
struct deductor {};
template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}
template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}
template<class T>
void baz(T, always_int<T>) {}
int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});
// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);
// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}
So... why does it usually work withvoid_t
? It's an alias template too.
– StoryTeller
Nov 28 at 7:46
@StoryTeller Because the deduction starts fromstd::void_t<Predicate<T>>>
,std::void_t
is already known.
– liliscent
Nov 28 at 7:48
Sorry, by usually I meantstd:void_t<decltype(std::declval<T>().foo)>
– StoryTeller
Nov 28 at 7:49
1
Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directlystd::void_t<declval<...>(...).foo>
, no? Sorry for flood of questions, I was just staring at these passages for too long.
– StoryTeller
Nov 28 at 7:54
1
Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
– StoryTeller
Nov 28 at 7:56
|
show 1 more comment
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
5
down vote
I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t
, here's an interesting explanation
Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.
template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};
template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());
live on compiler explorer
Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
– StoryTeller
Nov 28 at 7:02
@StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
– Guillaume Racicot
Nov 28 at 7:06
Might be. I'm examining partial ordering at the moment to see why.
– StoryTeller
Nov 28 at 7:08
@StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
– Guillaume Racicot
Nov 28 at 7:12
Oh I think I know why. Let me edit
– Guillaume Racicot
Nov 28 at 7:13
|
show 2 more comments
up vote
5
down vote
I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t
, here's an interesting explanation
Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.
template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};
template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());
live on compiler explorer
Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
– StoryTeller
Nov 28 at 7:02
@StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
– Guillaume Racicot
Nov 28 at 7:06
Might be. I'm examining partial ordering at the moment to see why.
– StoryTeller
Nov 28 at 7:08
@StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
– Guillaume Racicot
Nov 28 at 7:12
Oh I think I know why. Let me edit
– Guillaume Racicot
Nov 28 at 7:13
|
show 2 more comments
up vote
5
down vote
up vote
5
down vote
I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t
, here's an interesting explanation
Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.
template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};
template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());
live on compiler explorer
I cannot find the exact reason why your example don't work. If you want to dig more into the details of std::void_t
, here's an interesting explanation
Even if I cannot explain it in depth, I would like to add another reliable syntax that is used in the detection idiom.
template<
template <typename...> class Predicate,
typename T,
typename = void>
struct Resolve : std::false_type {};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, std::void_t<Predicate<T>>> : std::true_type {};
template <typename T>
using EnableIfHasFoo = decltype(std::declval<T>().foo());
live on compiler explorer
edited Nov 28 at 7:40
answered Nov 28 at 6:58
Guillaume Racicot
13.1k43163
13.1k43163
Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
– StoryTeller
Nov 28 at 7:02
@StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
– Guillaume Racicot
Nov 28 at 7:06
Might be. I'm examining partial ordering at the moment to see why.
– StoryTeller
Nov 28 at 7:08
@StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
– Guillaume Racicot
Nov 28 at 7:12
Oh I think I know why. Let me edit
– Guillaume Racicot
Nov 28 at 7:13
|
show 2 more comments
Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
– StoryTeller
Nov 28 at 7:02
@StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
– Guillaume Racicot
Nov 28 at 7:06
Might be. I'm examining partial ordering at the moment to see why.
– StoryTeller
Nov 28 at 7:08
@StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
– Guillaume Racicot
Nov 28 at 7:12
Oh I think I know why. Let me edit
– Guillaume Racicot
Nov 28 at 7:13
Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
– StoryTeller
Nov 28 at 7:02
Have an upvote for the minimal change. But I deleted mine because I'm still guessing at the reasons.
– StoryTeller
Nov 28 at 7:02
@StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
– Guillaume Racicot
Nov 28 at 7:06
@StoryTeller Thanks. I haven't found the reason either. It might be because the rule doesn't make the specialization more specialized.
– Guillaume Racicot
Nov 28 at 7:06
Might be. I'm examining partial ordering at the moment to see why.
– StoryTeller
Nov 28 at 7:08
Might be. I'm examining partial ordering at the moment to see why.
– StoryTeller
Nov 28 at 7:08
@StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
– Guillaume Racicot
Nov 28 at 7:12
@StoryTeller I added a link to another answer that explains void_t. However, even that answer don't seem to explain your case completely.
– Guillaume Racicot
Nov 28 at 7:12
Oh I think I know why. Let me edit
– Guillaume Racicot
Nov 28 at 7:13
Oh I think I know why. Let me edit
– Guillaume Racicot
Nov 28 at 7:13
|
show 2 more comments
up vote
5
down vote
The reason why your approach will fail is that Predicate<T>>
in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.
You can wrap your Predicate<T>>
to a non-deduced context to make it work:
template<class T>
struct identity {
using type = T;
};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};
Live Demo
Because inside a non-deduced context, the deduction won't happen for Predicate<T>
part, instead, it will use the Predicate
and T
obtained from elsewhere.
As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t
as a template alias, will be replaced by void
in deduction phase (see [temp.alias]/2), thus no deduction will happen.
Here are some examples to illustrate it more clearly:
template<class T>
using always_int = int;
template<template<class> class TT>
struct deductor {};
template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}
template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}
template<class T>
void baz(T, always_int<T>) {}
int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});
// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);
// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}
So... why does it usually work withvoid_t
? It's an alias template too.
– StoryTeller
Nov 28 at 7:46
@StoryTeller Because the deduction starts fromstd::void_t<Predicate<T>>>
,std::void_t
is already known.
– liliscent
Nov 28 at 7:48
Sorry, by usually I meantstd:void_t<decltype(std::declval<T>().foo)>
– StoryTeller
Nov 28 at 7:49
1
Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directlystd::void_t<declval<...>(...).foo>
, no? Sorry for flood of questions, I was just staring at these passages for too long.
– StoryTeller
Nov 28 at 7:54
1
Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
– StoryTeller
Nov 28 at 7:56
|
show 1 more comment
up vote
5
down vote
The reason why your approach will fail is that Predicate<T>>
in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.
You can wrap your Predicate<T>>
to a non-deduced context to make it work:
template<class T>
struct identity {
using type = T;
};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};
Live Demo
Because inside a non-deduced context, the deduction won't happen for Predicate<T>
part, instead, it will use the Predicate
and T
obtained from elsewhere.
As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t
as a template alias, will be replaced by void
in deduction phase (see [temp.alias]/2), thus no deduction will happen.
Here are some examples to illustrate it more clearly:
template<class T>
using always_int = int;
template<template<class> class TT>
struct deductor {};
template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}
template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}
template<class T>
void baz(T, always_int<T>) {}
int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});
// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);
// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}
So... why does it usually work withvoid_t
? It's an alias template too.
– StoryTeller
Nov 28 at 7:46
@StoryTeller Because the deduction starts fromstd::void_t<Predicate<T>>>
,std::void_t
is already known.
– liliscent
Nov 28 at 7:48
Sorry, by usually I meantstd:void_t<decltype(std::declval<T>().foo)>
– StoryTeller
Nov 28 at 7:49
1
Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directlystd::void_t<declval<...>(...).foo>
, no? Sorry for flood of questions, I was just staring at these passages for too long.
– StoryTeller
Nov 28 at 7:54
1
Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
– StoryTeller
Nov 28 at 7:56
|
show 1 more comment
up vote
5
down vote
up vote
5
down vote
The reason why your approach will fail is that Predicate<T>>
in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.
You can wrap your Predicate<T>>
to a non-deduced context to make it work:
template<class T>
struct identity {
using type = T;
};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};
Live Demo
Because inside a non-deduced context, the deduction won't happen for Predicate<T>
part, instead, it will use the Predicate
and T
obtained from elsewhere.
As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t
as a template alias, will be replaced by void
in deduction phase (see [temp.alias]/2), thus no deduction will happen.
Here are some examples to illustrate it more clearly:
template<class T>
using always_int = int;
template<template<class> class TT>
struct deductor {};
template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}
template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}
template<class T>
void baz(T, always_int<T>) {}
int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});
// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);
// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}
The reason why your approach will fail is that Predicate<T>>
in the third template parameter is not a non-deduced context. This causes the deduction to directly fail (see [temp.alias]/2), instead of using the deduced template arguments from elsewhere as in a non-deduced context.
You can wrap your Predicate<T>>
to a non-deduced context to make it work:
template<class T>
struct identity {
using type = T;
};
template <template <typename...> class Predicate, typename T>
struct Resolve<Predicate, T, typename identity<Predicate<T>>::type> : std::true_type {};
Live Demo
Because inside a non-deduced context, the deduction won't happen for Predicate<T>
part, instead, it will use the Predicate
and T
obtained from elsewhere.
As for why the usual detection-idiom (see Guillaume Racicot's answer) will work, it is because std::void_t
as a template alias, will be replaced by void
in deduction phase (see [temp.alias]/2), thus no deduction will happen.
Here are some examples to illustrate it more clearly:
template<class T>
using always_int = int;
template<template<class> class TT>
struct deductor {};
template<template<class> class TT, class T>
void foo(T, deductor<TT>) {}
template<template<class> class TT, class T>
void bar(T, deductor<TT>, TT<T>) {}
template<class T>
void baz(T, always_int<T>) {}
int main() {
// ok, both T and TT are deduced
foo(0, deductor<always_int>{});
// ERROR, TT<T> is NOT a non-deduced context, deduction failure
bar(0, deductor<always_int>{}, 0);
// ok, T is deduced, always_int<T> is replaced by int so no deduction
baz(0, 0);
}
edited Nov 28 at 9:12
answered Nov 28 at 7:42
liliscent
12.7k41639
12.7k41639
So... why does it usually work withvoid_t
? It's an alias template too.
– StoryTeller
Nov 28 at 7:46
@StoryTeller Because the deduction starts fromstd::void_t<Predicate<T>>>
,std::void_t
is already known.
– liliscent
Nov 28 at 7:48
Sorry, by usually I meantstd:void_t<decltype(std::declval<T>().foo)>
– StoryTeller
Nov 28 at 7:49
1
Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directlystd::void_t<declval<...>(...).foo>
, no? Sorry for flood of questions, I was just staring at these passages for too long.
– StoryTeller
Nov 28 at 7:54
1
Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
– StoryTeller
Nov 28 at 7:56
|
show 1 more comment
So... why does it usually work withvoid_t
? It's an alias template too.
– StoryTeller
Nov 28 at 7:46
@StoryTeller Because the deduction starts fromstd::void_t<Predicate<T>>>
,std::void_t
is already known.
– liliscent
Nov 28 at 7:48
Sorry, by usually I meantstd:void_t<decltype(std::declval<T>().foo)>
– StoryTeller
Nov 28 at 7:49
1
Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directlystd::void_t<declval<...>(...).foo>
, no? Sorry for flood of questions, I was just staring at these passages for too long.
– StoryTeller
Nov 28 at 7:54
1
Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
– StoryTeller
Nov 28 at 7:56
So... why does it usually work with
void_t
? It's an alias template too.– StoryTeller
Nov 28 at 7:46
So... why does it usually work with
void_t
? It's an alias template too.– StoryTeller
Nov 28 at 7:46
@StoryTeller Because the deduction starts from
std::void_t<Predicate<T>>>
, std::void_t
is already known.– liliscent
Nov 28 at 7:48
@StoryTeller Because the deduction starts from
std::void_t<Predicate<T>>>
, std::void_t
is already known.– liliscent
Nov 28 at 7:48
Sorry, by usually I meant
std:void_t<decltype(std::declval<T>().foo)>
– StoryTeller
Nov 28 at 7:49
Sorry, by usually I meant
std:void_t<decltype(std::declval<T>().foo)>
– StoryTeller
Nov 28 at 7:49
1
1
Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directly
std::void_t<declval<...>(...).foo>
, no? Sorry for flood of questions, I was just staring at these passages for too long.– StoryTeller
Nov 28 at 7:54
Yeah, it is (should really print that bullet list). But shouldn't the equivalence of alias template specializations make this moot? I.e., it should be as though the OP wrote directly
std::void_t<declval<...>(...).foo>
, no? Sorry for flood of questions, I was just staring at these passages for too long.– StoryTeller
Nov 28 at 7:54
1
1
Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
– StoryTeller
Nov 28 at 7:56
Oh. no wait. I see now. Great answer! That void_t we hide is failing immediately because the specialization needs to be replaced by its equivalent type!
– StoryTeller
Nov 28 at 7:56
|
show 1 more comment
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53513525%2ftemplate-template-class-predicate-not-working-in-partial-specialization%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
I cannot find the reason, but you need the partial specialization to be
Resolve<Predicate, T, std::void_t<Predicate<T>>
. Then at that point, you can also remove thestd::void_t
in the alias.– Guillaume Racicot
Nov 28 at 6:47
btw, your code look a lot like the dectection idiom
– Guillaume Racicot
Nov 28 at 6:49
@GuillaumeRacicot But then what is the predicate? Just decltype()? Very nice! Also, confusing why the above approach doesn't work.
– Curious
Nov 28 at 6:49
1
It can be an alias template to simply
decltype
. I don't know why, but thevoid_t
must be in the partial specialization. I think it has to do with how partial ordering work.– Guillaume Racicot
Nov 28 at 6:52